diff --git "a/docs/23_\354\213\244\355\226\211 \354\273\250\355\205\215\354\212\244\355\212\270/\353\202\230\354\204\270\355\230\204.md" "b/docs/23_\354\213\244\355\226\211 \354\273\250\355\205\215\354\212\244\355\212\270/\353\202\230\354\204\270\355\230\204.md" index 6451214b..a3a2be06 100644 --- "a/docs/23_\354\213\244\355\226\211 \354\273\250\355\205\215\354\212\244\355\212\270/\353\202\230\354\204\270\355\230\204.md" +++ "b/docs/23_\354\213\244\355\226\211 \354\273\250\355\205\215\354\212\244\355\212\270/\353\202\230\354\204\270\355\230\204.md" @@ -163,3 +163,144 @@ foo(); // 6 1. 환경 레코드(`EnvironmentRecord`): 스코프에 포함된 식별자를 관리하고 등록된 식별자에 바인딩된 값을 관리하는 저장소다. 2. 외부 렉시컬 환경에 대한 참조(`OuterLexicalEnvironmentReference`): 상위 스코프를 가리킨다. 상위 스코프란 외부 렉시컬 환경, 즉 해당 실행 컨텍스트를 생성한 소스코드를 포함하는 상위 코드의 렉시컬 환경을 말한다. + +### ✨ 23.6: 실행 컨텍스트의 생성과 식별자 검색 과정 + +```javascript +var x = 1; +const y = 2; + +function foo(a) { + var x = 3; + const y = 4; + + function bar(b) { + const z = 5; + console.log(a + b + x + y + z); + } + bar(10); +} + +foo(20); // 42 +``` + +#### 전역 객체 생성 + +전역 객체는 전역 코드가 평가되기 이전에 생성된다. +이때 전역 객체에는 빌트인 전역 프로퍼티와 빌트인 전역 함수, 그리고 표준 빌트인 객체가 추가되며 동작 환경에 따라 클라이언트 사이드 Web API 또는 특정 환경을 위한 호스트 객체를 포함한다. +전역 객체도 `Object.prototype`을 상속받는다. + +#### 전역 코드 평가 + +전역 코드 평가는 다음과 같은 순서로 이루어진다. + +1. 전역 실행 컨텍스트 생성 +2. 전역 렉시컬 환경 생성 + 1. 전역 환경 레코드 생성 + 1. 객체 환경 레코드 생성 + 2. 선언적 환경 레코드 생성 + 2. `this` 바인딩 + 3. 외부 렉시컬 환경에 대한 참조 결정 + +##### 📍 1. 전역 실행 컨텍스트 생성 + +비어 있는 전역 실행 컨텍스트를 생성해 실행 컨텍스트 스택에 푸시한다. + +##### 📍 2. 전역 렉시컬 환경 생성 + +전역 렉시컬 환경을 생성하고 전역 실행 컨텍스트에 바인딩한다. +이는 환경 레코드와 외부 렉시컬 환경에 대한 참조라는 두 개의 컴포넌트로 구성된다. + +##### 📍 2.1. 전역 환경 레코드 생성 + +전역 렉시컬 환경을 구성하는 컴포넌트인 전역 환경 레코드는 전역 변수를 관리하는 전역 스코프, 전역 객체의 빌트인 전역 프로퍼티와 빌트인 전역 함수, 표준 빌트인 객체를 제공한다. +`var` 키워드로 선언한 전역 변수(전역 객체의 프로퍼티가 됨)와 `let`, `const` 키워드로 선언한 전역 변수를 구분해 관리학 위해서 전역 스코프 역할을 하는 전역 환경 레코드는 **객체 환경 레코드**와 **선언적 환경 레코드**로 구분되어 있다. +객체 환경 레코드는 기존의 전역 객체가 관리하던 `var` 키워드로 선언한 전역 변수와 함수 선언문으로 정의한 전역 함수, 빌트인 전역 프로퍼티와 빌트인 전역 함수, 표준 빌트인 객체를 관리한다. +선언적 환경 레코드는 `let`, `const`로 선언한 전역 변수를 관리한다. +즉, 이 둘은 서로 협력해 전역 스코프와 전역 객체를 관리한다. + +##### 📍 2.1.1. 객체 환경 레코드 생성 + +객체 환경 레코드는 `BindingObject`라는 객체와 연결되는데, 이는 전역 객체 생성 시에 생성된 전역 객체이다. +**전역 코드 평가 과정에서 `var` 키워드로 선언한 전역 변수와 함수 선언문으로 정의된 전역 함수는 전역 환경 레코드의 객체 환경 레코드에 연결된 `BindingObject`를 통해 전역 객체의 프로퍼티와 메소드가 된다.** +이때 등록된 식별자를 전역 환경 레코드의 객체 환경 레코드에서 검색하면 전역 객체의 프로퍼티를 검색해 반환한다. +위 예제의 전역 변수 `x`와 전역 함수 `foo`는 이 과저을 통해 객체 환경 레코드의 `BindingObject`에 바인딩되어 있는 전역 객체의 프로퍼티가 메서드가 된다. + +##### 📍 2.1.2. 선언적 환경 레코드 생성 + +위 예제의 전역 변수 `y`는 `let`, `const` 키워드로 선언한 변수이므로 전역 객체의 프로퍼티가 되지 않기에 `window.y`처럼 전역 객체의 프로퍼티로서 참조할 수 없다. +또한 `const` 키워드로 선언한 변수는 선언 단계와 초기화 단계가 분리되어 진행되기 때문에 런타임에 실행 흐름이 변수 선언문에 도달하기 전까지 일시적 사각지대에 빠지게 된다. +**`let`, `const` 키워드로 선언한 변수도 변수 호이스팅이 발생하는 것은 변함이 없다.** +**하지만 이렇게 선언한 변수들은 런타임에 컨트롤이 변수 선언문에 도달할 때까지 초기화되지 않기 때문에 일시적 사각지대에 빠져서 참조할 수 없다.** + +##### 📍 2.2 `this` 바인딩 + +전역 환경 레코드의 `[[GlobalThisValue]]` 내부 슬롯에 `this`가 바인딩된다. +참고로 전역 환경 레코드의 구성 요소인 객체 환경 레코드와 선언적 환경 레코드에는 `this` 바인딩이 없다. +`this` 바인딩은 전역 환경 레코드와 함수 환경 레코드에만 존재한다. + +##### 📍 2.3 외부 렉시컬 환경에 대한 참조 결정 + +이는 상위 스코프를 가리키며, 이를 통해 단방향 링크드 리스트인 스코프 체인을 구현한다. +위 예제의 경우에는 전역 코드를 포함하는 소스코드는 없으므로 전역 렉시컬 환경의 외부 렉시컬 환경에 대한 참조에 `null`이 할당된다. + +#### 전역 코드 실행 + +이제 전역 코드가 순차적으로 실행되며, 변수 살당문이 실행되어 전역 변수 `x`, `y`에 값이 할당되고 `foo` 함수가 호출된다. +변수 할당문 또는 함수 호출문을 실행하려면 먼저 변수/함수 이름이 선언된 식별자인지 확인하는 과정이 필요하다. +**식별자 결정을 위해 식별자를 검색할 때는 실행 중인 실행 컨텍스트에서 식별자를 검색하기 시작하며, 없다면 상위 스코프로 이동해 검색한다.** + +#### `foo` 함수 코드 평가 + +함수 코드 평가는 아래 순서로 진행된다. + +1. 함수 실행 컨텍스트 생성 +2. 함수 렉시컬 환경 생성 + 1. 함수 환경 레코드 생성 + 2. `this` 바인딩 + 3. 외부 렉시컬 환경에 대한 참조 결정 + +##### 📍 1. 함수 실행 컨텍스트 생성 + +`foo` 함수 실행 컨텍스트를 생성해 실행 컨텍스트 스택의 최상위에 푸시한다. + +##### 📍 2. 함수 렉시컬 환경 생성 + +##### 📍 2.1. 함수 환경 레코드 생성 + +매개변수, `arguments` 객체, 함수 내부에서 선언한 지역 변수와 중첩 함수를 등록하고 관리한다. + +##### 📍 2.2. `this` 바인딩 + +함수 호출 방식에 따라 결정되며, `foo` 함수는 일반 함수로 호출되었으므로 `this`는 전역 객체를 가리킨다. + +##### 📍 2.3. 외부 렉시컬 환경에 대한 참조 결정 + +외부 렉시컬 환경에 대한 참조에 `foo` 함수 정의가 평가된 시점에 실행 중이었던 실행 컨텍스트의 렉시컬 환경의 참조가 결정된다. +이 경우에는 전역 코드 평가 시점에 평가되므로 전역 렉시컬 환경의 참조가 할당된다. +13.5절에서 **자바스크립트는 함수를 어디서 호출했는지가 아니라 어디에 정의했는지에 따라 상위 스코프를 결정한다고 했었다**! + +#### `foo` 함수 코드 실행 + +런타임이 시작되고, 매개변수에 인수가 할당되고 변수 할당문이 실행되어 지역 변수 `x`, `y`에 값이 할당된 후 함수 `bar`가 호출된다. + +#### `bar` 함수 코드의 평가 + +위에 기술한 과정과 동일하다. + +#### `bar` 함수 코드 실행 + +런타임이 시작되어 매개변수에 인수가 할당되고 변수 할당문이 실행되어 지역 변수 `z`에 값이 할당된다. +`bar` 함수 실행 컨텍스트에서 먼저 `console` 식별자를 겁색하고, 존재하지 않으므로 전역 렉시컬 환경으로 거슬러 올라가 `console`을 찾는다. +그 다음 `console`에 바인딩된 객체에서 `log` 메서드를 검색한 후, 표현식 `a + b + x + y + z`를 평가한다. +평가가 완료되면 생성한 값을 `console.log` 메서드에 전달하여 호출한다. + +#### `bar` 함수 코드 실행 종료 + +실행 컨텍스트 스택에서 `bar` 함수 실행 컨텍스트가 팝되어 제거된다. + +#### `foo` 함수 코드 실행 종료 + +#### 전역 코드 실행 종료 + +실행 컨텍스트 스택에는 아무것도 남아있지 않게 된다.