-
Execution Context 와 ECStack 내부
```javascript // global execution context function A(){ // A 함수 선언식 내부 // function execution context var x = 1; // AO.x function B(){ // B 함수 선언식 내부 // active function execution context var y = 2; // AO.y } B(); // AO.B() == null.B() } A(); // this.A(); ```
-
ECStack 내부
```javascript var ECStack = [ // B function execution context <B> activeFunctionExecutionContext: { AO(VO): { y: 2 }, Scope(Scope Chain): [ AO(VO): { y: 2 }, <A> functionExecutionContext.AO(VO): { x: 1, B: < reference to function > }, globalExecutionContext.VO: { A: < reference to function > } ] }, // A function execution context <A> functionExecutionContext: { AO(VO): { x: 1, B: < reference to function > }, Scope(Scope Chain): [ AO(VO): { x: 1, B: < reference to function > }, globalExecutionContext.VO: { A: < reference to function > } ] }, // global execution context globalExecutionContext: { VO: { A: < reference to function > }, Scope(Scope Chain): [ globalExecutionContext.VO ] } ]; ```
-
Global Execution Context 의 Scope Chain
-
Global Execution Context 내부에 생성되는 Scope Chain 은 globalExecutionContext.VO 만 포함한다.
globalExecutionContext:{ VO: ... Scope(Scope Chain): [ globalExecutionContext.VO ] }
-
-
함수 생명 주기 와 Function Execution Context 의 Scope Chain
-
함수 생명 주기는 함수 생성과 함수 호출로 나눌 수 있다.
-
함수 생성
-
함수 생성 시 [[Scope]] property 가 생성된다.<p>
-
[[Scope]] property 는 직접적인 접근이 불가능하며, 초기화된 값은 변경되지 않는다.<p>
-
[[Scope]] property 는 해당 Function Execution Context 상위에 있는 모든 부모 계층의 VO 를 갖는다.<p>
-
A 함수를 선언한다
// global execution context function A(){ }
-
함수 생성 시 함수 객체 내부
A: { [[Scope]]: [ // A 함수는 [[Scope]] property 를 통해 global execution context 의 VO 를 갖는다. globalExecutionContext.VO ]; }
-
함수 내부에 또 다른 함수 생성
// global execution context // 변수 선언 VO.x var x = 1; function A(){ // function execution context // 변수 선언 AO.y var y = 1; function B(){ // function execution context } } A();
-
각 함수 객체 내부
A: { [[Scope]]: [ // A 함수는 global execution context 의 VO 를 갖는다. globalExecutionContext.VO ]; } B: { [[Scope]]: [ // B 함수는 global execution context 의 VO 와 부모 함수인 A 함수의 AO(VO)를 갖는다. <A> functionExecutionContext.AO(VO), globalExecutionContext.VO ]; }
-
-
함수 호출
-
함수 호출 시, Function Execution Context 내부에 Scope Chain 이 추가된다.<p>
-
Scope Chain 은 함수 [[Scope]] property 를 그대로 가져가 초기화되며, Function Execution Context 내부 AO(VO)는 Scope Chain 의 **가장 앞**에 추가된다.
// global execution context var x = 1; function A(){ // function execution context var y = 2; } A(); // call A function object
-
ECStack 내부
var ECStack = [ // A function execution context <A> functionExecutionContext: { AO(VO): { y: 2 }, Scope(Scope Chain): [ // 함수 호출 시, 생성된 AO 는 Scope Chain 의 가장 앞(ScopeChain[0])에 추가되며, 식별자 검색 시 가장 먼저 검색된다. AO(VO): { y: 2 }, globalExecutionContext.VO: { x: 1, A: < reference to function > } ] }, globalExecutionContext: { VO: { x: 1, A: < reference to function > }, Scope(Scope Chain): [ globalExecutionContext.VO ] } ];
-
-
식별자 해석 과정
-
식별자 검색 순서는 **Scope Chain** 의 가장 **바닥**(ScopeChain[0])에서부터, 가장 **상위**(ScopeChain[scope.length]) 까지 검색 후, 그 값을 반환한다.
-
즉, 이와같은 검색 매커니즘에 의해, 해당 함수가 가진 AO(VO) 의 속성을 가장 빠르게 접근할 수 있으며, Global Execution Context 내부 VO 의 속성에 접근하는것이 가장 느린것이다.
// global execution context var x = 1; function A(){ // function execution context var y = 2; // functionExecutionContext.scopeChain.globalExecutionContext.VO.x console.log(x); // 1 // functionExecutionContext.scopeChain.AO.y console.log(y); // 2 } A();
-
ECStack 내부
var ECStack = [ <A> functionExecutionContext: { AO(VO): { y: 2 }, Scope(Scope Chain): [ AO(VO): { y: 2 }, globalExecutionContext.VO: { x: 1, A: < reference to function > } ] }, globalExecutionContext: { VO: { x: 1, A: < reference to function > }, Scope(Scope Chain): [ globalExecutionContext.VO ] } ];
-
만약, 각 Execution Context 에 선언된 식별자 이름이 같은 경우, 위에서 언급한 Scope Chain 의 검색 순서로 해당 값이 정해지게 된다.
-
BFunctionExecutionContext.AO.y
// global execution context var y = 1; function A(){ // function execution context var y = 2; function B(){ // active function execution context var y = 3; // BFunctionExecutionContext.AO.y console.log(y); // 3 } B(); // AO.B() === null.B(); } A();
-
AFunctionExecutionContext.AO.y
// global execution context var y = 1; function A(){ // function execution context var y = 2; function B(){ // active function execution context // B 함수에 선언된 y 변수를 삭제한다. // var y = 3; // AFunctionExecutionContext.AO.y console.log(y); // 2 } B(); // AO.B() === null.B(); } A();
-
globalExecutionContext.VO.y
// global execution context var y = 1; function A(){ // function execution context // A 함수에 선언된 y 변수를 삭제한다. // var y = 2; function B(){ // active function execution context // B 함수에 선언된 y 변수를 삭제한다. // var y = 3; // globalExecutionContext.VO.y console.log(y); // 1 } B(); // AO.B() === null.B(); } A();
-
ECStack 내부
```javascript var ECStack = [ <B> activeFunctionExecutionContext: { AO(VO): { y: 3 }, Scope(Scope Chain): [ AO(VO): { y: 3 }, <A> functionExecutionContext.AO(VO): { y: 2, B: < reference to function > }, globalExecutionContext.VO: { y: 1, A: < reference to function > } ] }, <A> functionExecutionContext: { AO(VO): { y: 2, B: < reference to function > }, Scope(Scope Chain): [ AO(VO): { y: 2, B: < reference to function > }, globalExecutionContext.VO: { y: 1, A: < reference to function > } ] }, globalExecutionContext: { VO: { y: 1, A: < reference to function > }, Scope(Scope Chain): [ globalExecutionContext.VO ] } ]; ```
-
또 다른 Scope Chain 예
```javascript // global execution context var num2 = 5; function add(num1, num2){ // function execution context // result 변수는 undefined 로 초기화된다. console.log(result); // undefined // functionExecution.AO.num1 // functionExecution.AO.num2 return num1 + num2; }; var result = add(5, 10); // 실행 코드 처리 후 result 변수에 15 가 할당된다. console.log(result); // 15 ``` - <span style="color:#c11f1f">ECStack</span> 내부 ```javascript var ECStack = [ <add> functionExecutionContext: { AO(VO): { arguments: { 0: 5, 1: 10 }, num1: 5, num2: 10 }, Scope(Scope Chain): [ AO(VO): { arguments: { 0: 5, 1: 10 }, num1: 5, num2: 10 }, globalExecutionContext.VO: { result: undefined, add: < reference to function >, num2: 5 } ] }, globalExecutionContext: { VO: { result: undefined, add: < reference to function >, num2: 5 }, Scope(Scope Chain): [ globalExecutionContext.VO ] } ]; ```
-
-
-
JS 에서 말하는 Closure란? 별도의 개념이 아닌, **Scope Chain 매커니즘**에 의한 바인딩 환경을 말하는것이다.<p>
-
일반적인 Closure 환경
```javascript // global execution context var x = 1; function A(){ // function execution context; // 식별자 검색을 통해, 상위 계층의 VO 에 접근하여 결과를 반환한다. // AFunctionExecutionContext.scopeChain[1].VO.x console.log(x); // 1 } A(); ```
-
하지만 Closure 환경으로 인해, 메모리 누수가 발생할 수 있다.
-
makeAdder 함수 호출 시, 반환받은 add5 함수 [[Scope]] 속성에는, 모든 부모 계층의 VO, AO(VO) 가 영구적으로 포함된다.
-
즉 makeAdder 함수(상위 계층) 종료 시, 소멸되는 AO(VO) 가, 해당 [[Scope]] 속성 내부에 영구적으로 보관된다는 것이다.(메모리 누수의 원인)
```javascript // global execution context function makeAdder(x) { // function execution context // 반환되는 익명함수에는 Scope Chain 매커니즘에 의해 부모 계층의 모든 VO 가 포함되어있다. // 부모 계층의 AO(VO)({x: 5}) return function(y) { // function execution context // Scope Chain 의 식별자 검색을 통해 부모 계층의 Vo.x 속성을 사용한다. // anonymousFunctionExecutionContext.scopeChain.makeAdderFunctionExecutionContext.AO.x // anonymousFunctionExecutionContext.scopeChain.AO.y return x + y; }; } var add5 = makeAdder(5); print(add5(2)); // 7 ```
-
anonymousFunction(= add5) 함수 [[Scope]] 속성 내부
```javascript anonymousFunction(= add5): { [[Scope]]: [ <makeAdder> functionExecutionContext.AO(VO): { arguments: { 0: 5 }, x: 5 }, globalExecutionContext.VO: { makeAdder: < reference to function >, add5: undefined } ] } ```
-
anonymousFunction(= add5) 함수 Scope Chain 내부
```javascript anonymousFunction(= add5): { Scope(Scope Chain): [ AO: { arguments: { 0: 2 }, y: 2 }, <makeAdder> functionExecutionContext.AO(VO): { arguments: { 0: 5 }, x: 5 }, globalExecutionContext.VO: { makeAdder: < reference to function >, add5: < reference to function > } ] } ```
-
-
-
Eval Execution Context 의 Scope Chain
-
eval execution context 의 Scope Chain 은 calling context 의 Scope Chain 을 따라간다.
-
즉 eval 함수가 포함된 execution context 의 Scope Chain 을 갖는다.
evalExecutionContext.Scope === callingContext.Scope;
-
실행 코드
// global execution context var x = 1; // global execution context 의 Scope Chain 을 갖는다. eval('console.log(x)'); // 1 function A(){ // function execution context var y = 2; // A function execution context 의 Scope Chain 을 갖는다. eval('console.log(x, y)'); // 1 2 function B(){ // B function execution context 의 Scope Chain 을 갖는다. var z = 3; eval('console.log(x, y, z)'); // 1 2 3 } B(); } A();
-
-
Function 생성자 함수
-
Function 생성자 함수를 통한, 함수 생성 시 생성되는 [[Scope]] property 에는 Global Execution Context 의 VO 만 포함된다.
// global execution context var x = 1; var y = 2; var fn = Function('console.log(x, y);'); // 1, 2 fn();
-
globalExecutionContext 의 x 변수에 접근
// global execution context var x = 1; function A(){ // function execution context var y = 2; var fn = Function('console.log(x);'); // 1 fn(); } A();
-
functionExecutionContext 의 y 변수에 접근
// global execution context var x = 1; function A(){ // function execution context // AFunctionExecutionContext.AO.y var y = 2; var fn = Function('console.log(y);'); // Uncaught ReferenceError: y is not defined fn(); } A();
-
-
-
Prototype Chain 확장 검색
-
만약 Scope Chain 내부 VO 에 원하는 식별자 이름이 없을경우, VO 객체의 원형인 Object.prototype 까지 검색 후 찾아낸다.
-
즉 Global Execution Context 내부 VO 는 그 원형인 Object.prototype 객체를 참조할 수 있다.
// global execution context function A(){ // 어떤 execution context 의 VO 에도 x 속성이 선언하지 않았다. // function execution context // globalExecutionContext.VO.__proto__.x 속성을 참조한다. console.dir(x); // 1 } // Object 객체의 원형을 확장한다. Object.prototype.x = 1; A();
-
ECStack 내부
var ECStack = [ functionExecutionContext: { // A function object AO(VO): { }, Scope(Scope Chain): [ AO(VO): { }, globalExecutionContext.VO: { A: < reference to function >, __proto__: { x: 1 } } ] }, globalExecutionContext: { VO: { A: < reference to function >, __proto__: { x: 1 } }, Scope(Scope Chain): [ globalExecutionContext.VO ] } ];
-
-
-
Scope Chain 을 확장하는 with 문 과 catch 절
-
with 문
-
with 문을 만나면, 해당 Execution Context 의 Scope Chain 은 전달된 인자로 확장된다.<p>
-
실행 코드
// global execution context // globalExecutionContext.VO.person var person = { name: 'Nicholas', age: 30 }; // globalExecutionContext.VO.num2 var num2 = 10; // globalExecutionContext.VO.displayInfo function displayInfo(){ // function execution context // displayInfoFunctionExecutionContext.AO.count var count = 5; // with 문에 전달된 인자(person)로 해당 Scope Chain 이 확장된다. with ({name: 'Angel', age: 18}){ // 확장된 Scope Chain 을 통해 객체 속성에 접근한다. // displayInfoFunctionExecutionContext.scopeChain.withObject.name console.log(name); // 'Angel' // displayInfoFunctionExecutionContext.scopeChain.withObject.age console.log(age); // 18 } }; displayInfo();
-
ECStack 내부
var ECStack = [ <displayInfo> functionExecutionContext: { AO(VO): { arguments: { }, count: 5 }, Scope(Scope Chain): [ // with 문으로 인해, Scope Chain 이 확장된다.(Scope Chain 의 가장 앞(ScopeChain[0])에 추가된다. with(VO(person)): { name: 'Angel', age: 18 }, AO(VO): { arguments: { }, count: 5 }, globalExecutionContext.VO: { person: { name: 'Nicholas', age: 30 }, displayInfo: < reference to function >, num2: 10 } ] }, globalExecutionContext: { VO: { person: { name: 'Nicholas', age: 30 }, displayInfo: < reference to function >, num2: 10 }, Scope(Scope Chain): [ globalExecutionContext.VO ] } ];
-
-
catch 절
-
catch 절을 만나면, 해당 Execution Context 의 Scope Chain 은 전달된 인자(ex)로 확장된다.<p>
```javascript
// global execution context try{ a; // error } catch(ex){ // globalExecutionContext.scopeChain.catchObject(ex).message console.log(ex.message); } ```
-
ECStack 내부
```javascript var ECStack = [ globalExecutionContext: { VO: { }, Scope(Scope Chain): [ catch(VO(<exception object>)): { message: 'a is not defined', ... }, globalExecutionContext.VO: { } ] } ]; ```
-
-