Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes.
183 changes: 183 additions & 0 deletions Chapter_19/GeonHo/19.8~19.14 정리.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
## 19.8 오버라이딩과 프로퍼티 섀도잉

`오버라이딩`이란? <br>

- 상위 클래스의 메서드를 하위 클래스에서 재정의하는 것을 말한다.

`프로퍼티 섀도잉`이란? <br>

- 프로토타입 프로퍼티와 같은 이름의 프로퍼티를 인스턴스에 추가하면, 프로토타입 프로퍼티를 덮어쓰는 것이 아닌 인스턴스 프로퍼티로 추가된다.
- 이렇게 프로퍼티를 오버라이딩하여 상속 관계에 의한 프로퍼티가 가려지는 현상이 프로퍼티 섀도잉이다.

<br>

**인스턴스 등의 하위 객체를 통해 상위 프로토타입의 프로퍼티를 변경 또는 삭제하는 것은 불가능하다.** <br>
다시 말해, get은 허용되나 set은 허용되지 않는다

<br>

## 19.9 프로토타입의 교체

프로토타입은 임의의 다른 객체로 변경할 수 있다. <br>
생성자 함수의 prototype 또는 인스턴스의 **proto** 접근자 프로퍼티를 통해 프로토타입을 교체할 수 있다.

```js
const Person = (function () {
function Person(name) {
this.name = name;
}

// Person 생성자 함수의 프로토타입을 아래 객체로 교체하였다.
Person.prototype = {
sayHello() {
console.log(`Hi! My name is ${this.name}`);
},
};

return Person;
})();

const me = new Person("Lee");
```

위 코드에서 Person 생성자 함수의 프로토타입을 새로운 객체로 교체했다. <br>
다만, 위 프로토타입 객체는 constructor 프로퍼티가 없다. <br>
때문에 인스턴스 me는 생성자 함수가 Person이 아닌 Object다.

```js
console.log(me.constructor === Person); // false
console.log(me.constructor === Object); // true
```

<br>

생성자 함수가 Person을 가르키게하려면 아래와 같이 객체를 수정하면 된다.

```js
Person.prototype = {
constructor: Person,
sayHello() {
console.log(`Hi! My name is ${this.name}`);
},
};
```

위와 같이 생성자 함수를 활용한 프로토타입 교체 방식과 인스턴스의 **proto** 접근자 프로퍼티를 활용한 프로토타입 교체 방식은 조금 다르다. <br>
생성자 함수의 경우, 생성자함수는 생성자 함수.prototype을 가르키지만 인스턴스의 **proto** 접근자 프로퍼티를 사용한 방식은 그렇지 않다. <br>

<br>

## 19.10 instanceof 연산자

```js
객체 instanceof 생성자 함수
```

instanceof 연산자는 생성자 함수의 prototype에 바인딩된 객체가 객체의 프로토타입 체인 상에 존재하면 true를 반환한다. <br>

<br>

## 19.11 직접 상속

## 19.12 정적 프로퍼티/메서드

어떤 생성자 함수 객체가 소유한 프로퍼티/메서드를 뜻한다.

정적 프로퍼티/메서드는 new로 생성된 인스턴스도 참조할 수 없다. <br>
왜냐하면 인스턴스로 참조할 수 있는 프로퍼티/메서드는 프로토타입 체인 상에 존재해야하기 때문인데, <br>
정적 프로퍼티/메서드는 생성자 함수 객체에 바인딩되어 있기 때문이다.

정적 프로퍼티/메서드와 프로토타입 프로퍼티/메서드의 차이점은 <br>
`생성자함수.fn()` 인지 `생성자함수.prototype.fn()` 로 구분할 수 있다.

<br>

## 19.13 프로퍼티 존재 확인

### in

in 연산자는 확인 대상 객체 뿐 아니라, 상속받은 모든 프로토타입 프로퍼티를 확인한다.

```js
const person = {
name: "Lee",
address: "Seoul",
};

console.log("name" in person); // true
console.log("address" in person); // true
console.log("age" in person); // false
console.log("toString" in person); // true
// toString은 Object.prototype의 메서드이다.
// person 객체가 Object.prototype을 상속받았으므로 true를 리턴한다.
```

### hasOwnProperty

상속받은 프로퍼티의 경우 false를 리턴, 고유 프로퍼티의 key인 경우에만 true를 리턴한다.

<br>

## 19.14 프로퍼티 열거

### for...in

for...in 문은 객체의 프로퍼티를 열거할 때 사용한다. <br>
in 연산자와 마찬가지로 상속받은 프로토타입의 프로퍼티까지 열거한다.

해서 vsc의 for...in 문의 snippet을 보면 다음과 같이 hasOwnProperty 메서드가 포함되어 있다.

```js
for (const key in object) {
if (Object.hasOwnProperty.call(object, key)) {
const element = object[key];
...
}
}
```

다만, Object.prototype의 toString 같은 프로퍼티는 열거하지 않는다. <br>

왜일까? <br>
이는 프로퍼티의 열거 가능 여부를 판별하는 `내부 슬롯 [[Enumerable]]`이 false이기 때문이다.

또한, for...in은 열거시 순서를 보장하지 않는다. <br>
다만, 대부분의 브라우저에서는 순서를 보장하고, *숫자인 프로퍼티 키에 대해서는 정렬*을 한다.

<br>

객체 자신의 고유 프로퍼티만 열거하기 위해서는 for...in 문 보다는 Object.keys / values / entries 메서드를 사용하는 것이 좋다.

### Object.keys

```js
const person = {
name: "Lee",
address: "Seoul",
__proto__: { age: 20 },
};

console.log(Object.keys(person)); // ['name', 'address']
```

객체 자신의 열거 가능한 프로퍼티 키를 **배열**로 반환한다.

<br>

### Object.values

```js
console.log(Object.values(person)); // ['Lee', 'Seoul']
```

'' value를 배열로 반환한다.

<br>

### Object.entries

```js
console.log(Object.entries(person)); // [['name', 'Lee'], ['address', 'Seoul']]
```

'' key, value를 배열로 반환한다.
Binary file added Chapter_23/GeonHo/img/image-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Chapter_23/GeonHo/img/image-2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Chapter_23/GeonHo/img/image-3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Chapter_23/GeonHo/img/image-4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Chapter_23/GeonHo/img/image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
83 changes: 83 additions & 0 deletions Chapter_23/GeonHo/정리.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# 23장 실행 컨텍스트

1. 소스코드의 타입
- 전역 코드
- 함수 코드
- eval 코드
- 모듈 코드
2. 소스코드의 평가와 실행
모든 소스코드는 실행 전 평과 과정을 거친다.
즉 JS 엔진은 소스코드를 '평가' 와 '실행' 과정으로 나누어 처리한다.

'평가'과정에서는 실행 컨텍스트를 생성하고, 변수나 함수 선언문만 실행하여
실행 컨텍스트가 관리하는 스코프(렉시컬 환경의 환경 레코드)에 등록한다

평가 과정 이후 '실행'과정이 시작된다. 즉 런타임이 시작된다.
변수나 함수에 대한 정보는 실행 컨텍스트가 관리하는 스코프에서 가져온다.
또 변수 값의 변경 등은 다시 스코프에 등록한다.

3. 실행 컨텍스트의 역할

![alt text](img/image.png)

위와 같은 상황에서, 각 코드들을 스코프에 맞게 구분하여 관리하게 위하여
스코프, 식별자, 코드 실행 순서 등의 관리가 필요하다.

이를 위해 실행 컨텍스트가 존재한다.

실행 컨텍스트는 식별자를 등록하고 관리하는 스코프와 코드 실행 순서 관리를 구현한 내부 매커니즘으로,
모든 코드는 실행 컨텍스트를 통해 실행되고 관리된다.

식별자와 스코프는, 실행 컨텍스트의 렉시컬 환경으로 관리하고,
코드 실행 순서는 실행 컨텍스트 스택으로 관리한다.

4. 실행 컨텍스트의 스택
JS 엔진은 먼저 전역 코드를 평가하여 '전역' 실행 컨텍스트를 생성한다.
그러다 함수가 호출되면, 함수 코드를 평가하여 '함수' 실행 컨텍스트를 생성한다.
이렇게 생성된 실행 컨텍스트는 스택 자료구조로 관리되는데, 이를 실행 컨텍스트 스택이라고 한다.
5. 렉시컬 환경
실행 컨텍스트가 코드 실행 순서를 관리한다면,
렉시컬 환경은 스코프와 식별자를 관리한다.
![alt text](img/image-1.png)

사진과 같이, 렉시컬 환경은 key | value 형태의 객체 스코프이다.
실행 컨텍스트는 렉시컬 환경과 변수 환경으로 구성되어있는데,
일단 간략하게 배우기 위해, 두개를 합쳐 렉시컬 환경이라 하겠다.

렉시컬 환경은 두 개의 컴포넌트로 구성된다.

![alt text](img/image-2.png)

1. 환경레코드
스코프에 포함된 식별자와 바인딩 된 값을 관리한다.

2. 외부 렉시컬 환경에 대한 참조
상위 스코프, 즉 해당 실행 컨텍스트를 생성한 소스코드를 포함하는 렉시컬 환경을 가르킨다.

6. 실행 컨텍스트의 생성과 식별자 검색 과정

![alt text](img/image-3.png)

위 코드의 실행 컨텍스트와 렉시컬 환경에 대해 아라보자.

![alt text](img/image-4.png)

우선 전역 먼저.
전역 실행 환경에서 전역 렉시컬 환경을 생성하고 전역 렉시컬 환경을 바인딩.
전역 환경 레코드는 var같은 객체 환경 레코드와 const, let 같은 선언 환경 레코드를 분리해서 바인딩한다.
var의 경우 변수의 선언 단계와 초기화 단계가 동시에 진행되다보니 전역 객체에 바로 undefined가 할당된다.

허나 let, const는 선언 단계 후 초기화 단계가 분리되어 실행되기에,
런타임이 변수 선언문에 닿아 값이 할당되기 전 까지, 변수에 접근할 수 없는 일시적 사각지대에 빠지게 된다.
때문에 y에 <uninitialized>가 바인딩되어있다.

this도 전역 환경 레코드 내부 슬롯에 바인딩 된다.

외부 렉시컬 환경 참조에는 null 이 할당된다. 왜냐면 전역이라 참조할 곳이 없기 때문.

이렇게 전역 실행 컨텍스트와 렉시컬 환경에 대하여 알아보았다.
마지막으로 전역 코드가 실행되면 x, y에 값이 할당되고, foo 함수가 호출된다.

소스코드를 보면, 동일한 이름의 식별자가 각기 다른 스코프에 존재할 수 있다.
이럴때 JS는 식별자 결정을 위해 실행중인 실행 컨텍스트에서부터 식별자를 검색하기 시작한다.
이때 식별자를 못찾으면, 외부 렉시컬 환경이 가르키는 상위 스코프로 이동하여 식별자를 검색한다.
Loading