diff --git "a/[23\354\236\245] \354\213\244\355\226\211 \354\273\250\355\205\215\354\212\244\355\212\270/\352\261\264\354\232\260.md" "b/[23\354\236\245] \354\213\244\355\226\211 \354\273\250\355\205\215\354\212\244\355\212\270/\352\261\264\354\232\260.md" new file mode 100644 index 0000000..2ef6cf3 --- /dev/null +++ "b/[23\354\236\245] \354\213\244\355\226\211 \354\273\250\355\205\215\354\212\244\355\212\270/\352\261\264\354\232\260.md" @@ -0,0 +1,208 @@ +# 실행 컨텍스트를 알아야 하는 이유 +1. JS가 식별자와 식별자에 바인딩 된 값을 관리하는 방식을 알기 위함 +2. **호이스팅**이 왜 발생하는지 알기 위해서 +3. **클로저**의 동작 방식을 알기 위해서 +4. **이벤트 핸들러**와 **비동기 처리**의 동작 방식을 알기 위해서 + +# 소스코드 평가와 실행 ++ JS에서는 소스코드를 평가과정과 실행과정으로 나누어서 실행합니다. ++ 평가 과정에서는 실행 컨텍스트를 생성하고 선언문만 먼저 실행하여 변수, 함수 식별자를 키로 실행 컨텍스트가 관리하는 스코프에 등록합니다. ++ 평가 과정이 끝나면 선언문을 제외한 소스코드를 순차적으로 실행합니다. ++ 실행 과정에서 변수 값의 변경 등, 실행 결과는 다시 실행 컨텍스트에 반영됩니다. + +# 어떨때 실행 컨텍스트를 생성하는지? +1. 전역공간은 자동으로 컨텍스트로 구성된다. +2. 함수를 실행한다. +3. eval()함수를 실행한다. +4. block을 만든다 (ES6+) +5. 모듈 코드는 모듈 별로 독립적인 컨텍스트를 생성. + +# 실행 컨텍스트의 역할 +```js +const x = 1; +const y = 2; + +function foo(a){ + const x = 10; + const y = 20; + + console.log(a + x + y); +} + +foo(100); + +console.log(x + y); +``` +<br> +이런식으로 코드가 실행되려면 스코프, 식별자, 코드 실행 순서등의 관리가 필요합니다. 이러한 관리를 하는 객체가 바로 실행 컨텍스트 입니다. + +# 실행 컨텍스트 정의 ++ 소스코드를 실행하는데 필요한 환경을 제공하고 실행 결과를 관리하는 영역입니다. ++ 스코프와 실행 순서 관리를 구현한 내부 메커니즘입니다. ++ 식별자와 스코프는 렉시컬 환경으로, 코드 실행 순서는 실행 컨텍스트 스택으로 관리합니다. + +# 실행 컨텍스트 스택 +```js +const x = 1; + +function foo(){ +//... + function bar(){ + //... + } + + bar(); + +} + +foo(); + +함수 실행 순서는 전역 -> foo -> bar -> foo -> 전역 +``` +<br> ++ 실행 컨텍스트 스택은 코드 실행 순서를 관리합니다. ++ 스택의 최상위에 있는 실행 컨텍스트는 언제나 현재 실행 중인 실행 컨텍스트입니다. ++ 평가과정이 끝나고 실행 컨텍스트가 생성되면 스택에 푸쉬합니다. ++ 실행과정이 끝나면 스택에서 해당 실행 컨텍스트를 팝 합니다. + +# 렉시컬 환경 ++ 책의 방식과 같이 LexicalEnvironment 와 VariableEnvironment 를 구분하지 않고 렉시컬 환경으로 통일해서 설명하겠습니다. ++ 렉시컬 환경은 식별자, 식별자에 바인딩 된 값, 상위 스코프 참조를 기록하는 자료구조 입니다. 실행 컨텍스트를 구성하는 일원입니다. ++ 렉시컬 환경은 환경 레코드, 외부 렉시컬 환경에 대한 참조로 구성됩니다. +<br> ++ 환경 레코드는 스코프에 포함된 식별자를 등록하고 등록된 식별자에 바인딩 된 값을 관리하는 저장소입니다. ++ 외부 렉시컬 환경에 대한 참조는 상위 스코프를 가르킵니다. 스코프 체인을 구현하는 메커니즘입니다. + +# 전역 실행 컨텍스트 +```js +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); +``` +<br> + +### 호이스팅 ++ 전역 코드 평가 과정에서 var 키워드로 선언한 전역 변수와 함수 선언문은 BindingObject를 통해 전역 객체의 프로퍼티, 메서드가 됩니다. ++ var 변수로 선언한 변수는 전역 객체의 프로퍼티가 되어 암묵적으로 undefined가 바인딩 됩니다. (변수 호이스팅) ++ 함수 선언문은 그 함수 이름과 동일한 식별자를 BindingObject를 통해 전역 객체의 키로 등록하고 생성된 함수 객체를 즉시 할당합니다. (함수 호이스팅) + +```js +let foo = 1; + +{ + console.log(foo); //Error + let foo = 2; +} +``` ++ 선언적 환경 레코드는 let, const 키워드로 선언된 식별자를 관리합니다. ++ let, const 키워드로 선언한 식별자는 선언단계와 초기화 단계가 분리되어 선언문에 도달하기 전까지 일시적 사각지대에 빠지게 되어 참조 불가능한 상태가 됩니다. ++ 초기화 단계는 런타임때 선언문에 도달했을때 시작됩니다. ++ let, const 키워드도 호이스팅이 일어나지만, 선언전에 참조하면 에러가 발생하도록 설정되었다고 보면 됩니다. + +### 식별자 검색 ++ 변수 할당문, 함수 호출문을 실행하기 위해선 해당 변수, 함수이름이 선언된 식별자인지 확인해야 합니다. ++ 만약 현재 렉시컬 환경에서 검색이 불가능하면 외부 렉시컬 참조를 타고 올라갑니다, 즉 상위 스코프를 타고 올라가서 식별자를 검색합니다. ++ 상위에서도 식별자를 찾지 못했다면 참조 에러를 발생시킵니다. + +# 함수 실행 컨텍스트 +```js +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); +``` +<br> + +### 하나의 환경 레코드 ++ 전역 환경 레코드는 객체 환경 레코드, 선언적 환경 레코드로 분리되었었습니다. ++ 하지만 함수 환경 레코드는 별도로 분리되지 않습니다. 따라서, var, let, const 모두 하나의 환경 레코드에 집어넣습니다. + +### this 바인딩 ++ 내부슬롯 [[ThisValue]]에 바인딩될 this는 함수 호출 방식에 따라 결정됩니다. ++ foo는 일반함수로 호출되었으므로 this는 전역 객체를 가르킵니다. ++ 만약 new와 함께 생성자 함수로 호출되었다면 해당 인스턴스를 가르키게 되겠지요. + +### 상위 스코프 결정 ++ 함수의 상위 스코프는 함수를 어디서 호출했느냐에 따라 결정하지 않고 어디서 정의했느냐에 따라 상위 스코프를 결정합니다. ++ 함수 정의를 평가할때 현재 실행중인 실행 컨텍스트의 렉시컬 환경을 함수 객체 [[Environment]]에 저장합니다. ++ 추후 함수가 실행될때 외부 렉시컬 환경에 대한 참조에 [[Environment]]에 저장된 객체가 할당되는 것이지요. ++ 나중에 클로저를 이해할때 중요한 개념이라고 합니다. +```js +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); +``` +<br> + +### 가비지 컬렉터 ++ bar 함수 실행 컨텍스트가 스택에서 제거 되었을때, bar 함수 렉시컬 환경 까지 즉시 소멸하진 않습니다. ++ bar 함수의 렉시컬 환경을 누군가가 참조하고 있다면 가비지 컬렉터의 대상이 되지 않습니다. ++ 가비지 컬렉터는 누구에게도 참조되고 있지 않은 메모리 공간을 지웁니다. ++ 클로저의 단서가 될 것 같습니다. + +# 블록 레벨 스코프 +```js +let x = 1; +if (true){ + let x = 10; + console.log(x); // 10 +} +console.log(x); // 1 +``` + +<br> ++ let, const 키워드로 선언된 변수는 블록 레벨 스코프를 따릅니다. ++ 이에반해 var 키워드로 선언된 변수는 함수 레벨 스코프를 따릅니다. ++ 블록을 만나게 되면 선언적 환경 레코드를 갖는 렉시컬 환경을 새롭게 생성하고 현재 실행중인 컨텍스트의 렉시컬환경을 교체합니다. + +```js +console.log(a); // undefined + +{ + var a = 10; + let b = 20; +} + +console.log(a); // 10 +console.log(b); // 참조 에러 +``` ++ 블록안에 있는 var 키워드의 변수는 블록 렉시컬 환경에 포함되지 않음을 알 수 있습니다. + +# 정리 +1. 실행 컨텍스트는 실행하는데에 필요한 환경을 제공하고 코드의 실행 결과를 관리한다. +2. 스코프와 코드 실행 순서 관리를 구현한 내부 메커니즘이다. +3. 식별자, 스코프는 렉시컬 환경으로 관리한다. +4. 실행 순서는 실행 컨텍스트 스택으로 관리한다. +5. 렉시컬 환경은 환경 레코드와 외부 렉시컬 환경에 대한 참조로 구성되며 환경 레코드는 식별자를, 외부 렉시컬 환경 참조는 상위 스코프를 관리한다.