# 실행 컨텍스트 간단하게: 코드를 실행하는데 필요한 환경(변수, 함수 선언, scope, this) 3가지 객체가 존재함 1. **Variable Object** - 변수, 매개변수(parameter)와 인수 정보(argument), 함수 선언(함수 표현식은 제외)를 담는 객체 - 전역 컨텍스트의 경우 VO는 모든 전역 변수, 전역 함수 등을 포함하는 전역 객체(Global Object)를 가리킴 - 함수 컨텍스트의 경우 VO는 Activation Object를 가리키며 parameter와 argument를 배열의 형태로 담고 있는 arguments object가 추가됨 2. **Scope Chain** - 변수, 함수 선언 등의 정보를 담고 있는 전역 객체(AO) 또는 활성 객체(AO)의 리스트를 가리킨다. - 엔진은 scope chain을 통해 lexical scope를 확인한다. 3. **This Value** - this 값이 할당되며 this는 함수 호출 패턴에 의해 결정된다. ## 실행 컨텍스트 동작 과정 ### 코드 예시 ```javascript var y = 'yyy'; function outerFunc() { var x = 10; var innerFunc = function(number) { x += number; console.log(x); }; return innerFunc; } /* * 함수 outerFunc를 호출하면 내부 함수 innerFunc가 반환된다. * 그리고 함수 outerFunc의 실행 컨텍스트는 소멸한다. */ var inner = outerFunc(); inner(5); // 15 ``` ### 동작 과정 ![image](https://gist.githubusercontent.com/ji3427/308e58e69c5f5385af26efe704813d6d/raw/e43064234cf0e60f0e79dbbeb1dbf33c65ab69f4/SSlide1.jpg) 실행 컨텍스트에 진입하기 이전에 전역객체 GO(Global Object)가 생성된다. 이 객체에는 DOM,BOM, built-in-object 등이 설정되어 있다. ![image](https://gist.githubusercontent.com/ji3427/308e58e69c5f5385af26efe704813d6d/raw/e43064234cf0e60f0e79dbbeb1dbf33c65ab69f4/SSlide2.jpg) 전역 코드가 실행되기 전 Excution Context가 생성되고 스택에 쌓인다. 이 Excution Context를 바탕으로 아래 3가지 처리가 실행된다. - 스코프 체인의 생성과 초기와 - Variable Instatiation(변수 객체화) 실행 - this value 결정 ![image](https://gist.githubusercontent.com/ji3427/308e58e69c5f5385af26efe704813d6d/raw/e43064234cf0e60f0e79dbbeb1dbf33c65ab69f4/SSlide3.jpg) Caller(함수가 선언된 컨텍스트)의 Scope Chain이 참조하고 있는 객체가 스코프 체인에 push된다. 지금은 전역 실행 컨텍스트이기 때문에 전역객체(GO)만을 가진 리스트가 생성된다. ![image](https://gist.githubusercontent.com/ji3427/308e58e69c5f5385af26efe704813d6d/raw/e43064234cf0e60f0e79dbbeb1dbf33c65ab69f4/SSlide4.jpg) Variable Instantiation(변수 객체화)은 Variable Object에 프로퍼티와 값을 추가하는 것을 의미한다. Variable Instantiation은 아래의 순서로 VO에 프로퍼티와 값을 set한다. 1. (Function Code인 경우) 매개변수(parameter)가 프로퍼티로, 인수(argument)가 값으로 설정된다. 2. 함수 선언(함수 표현식 제외)을 대상으로 함수명이 프로퍼티로, 생성된 함수 객체가 값으로 설정된다.(함수 호이스팅) 3. 변수 선언을 대상으로 변수명이 프로퍼티로, undefined가 값으로 설정된다.(변수 호이스팅) ```javascript console.log(number); // undefined var number = 10; console.log(number); // 10 ``` javascript에서 위와같이 변수가 선언되기 전에 변수에 접근해도 에러가 발생하지 않는데 이를 호이스팅이라고 한다.이는 Variable Instantiation단계에서 이미 변수와 함수가 설정되어 있기때문이다. ![image](https://gist.githubusercontent.com/ji3427/308e58e69c5f5385af26efe704813d6d/raw/e43064234cf0e60f0e79dbbeb1dbf33c65ab69f4/SSlide5.jpg) this value는 default값으로 전역 객체(GO)를 가리키고 있다. 이후 함수 호출 패턴에 의해 this에 값이 결정된다. 본문 this문서 참조 ![image](https://gist.githubusercontent.com/ji3427/308e58e69c5f5385af26efe704813d6d/raw/e43064234cf0e60f0e79dbbeb1dbf33c65ab69f4/SSlide6.jpg) ```javascript var y = 'yyy'; ``` 코드가 실행되면서 변수에 값이 결정된다. 전역 변수 y에 문자열 ‘yyy’를 할당할 때, 현재 실행 컨텍스트의 스코프 체인이 참조하고 있는 Variable Object를 선두(0)부터 검색하여 변수명에 해당하는 프로퍼티가 발견되면 값(‘yyy’)을 할당한다. ![image](https://gist.githubusercontent.com/ji3427/308e58e69c5f5385af26efe704813d6d/raw/e43064234cf0e60f0e79dbbeb1dbf33c65ab69f4/SSlide7.jpg) ``` javascript var inner = outerFunc(); ``` 코드를 실행하면서 함수를 수행하는 경우 새로운 실행 컨텍스트가 생성된다. 위의 코드에서는 outerFunc함수가 실행되면서 새로운 Execution Context가 생성된다. 생성 후 스코프 체인을 생성하고 초기화 한다. outerFunc의 경우 lexical scope가 전역이기 때문에 scope chain은 전역 객체(GO)와 자신의 활성 객체(AO)를 가지는 리스트를 가리킨다. ![image](https://gist.githubusercontent.com/ji3427/308e58e69c5f5385af26efe704813d6d/raw/e43064234cf0e60f0e79dbbeb1dbf33c65ab69f4/SSlide8.jpg) 스코프 체인의 생성과 초기화에서 생성된 활성객체(AO)를 VO가 가리키게 된다. OuterFunc의 변수들을 생성하고 초기화한다. 위의 코드에서는 x, innerFunc가 생성되고 undefined로 초기화된다. ※ 함수 선언식의 경우 함수가 프로퍼티에 추가되고 함수 객체로 즉시 초기화되는 반면에 함수 표현식은 변수의 규칙을 따른다. 그렇기 때문에 undefined로 초기화된다. 함수 선언식은 호이스팅이 되고 함수 표현식은 호이스팅이 안되는 이유이다. ![image](https://gist.githubusercontent.com/ji3427/308e58e69c5f5385af26efe704813d6d/raw/e43064234cf0e60f0e79dbbeb1dbf33c65ab69f4/SSlide9.jpg) this가 전역 객체(GO)를 가리키게 된다. ![image](https://gist.githubusercontent.com/ji3427/308e58e69c5f5385af26efe704813d6d/raw/e43064234cf0e60f0e79dbbeb1dbf33c65ab69f4/SSlide10.jpg) ```javascript function outerFunc() { var x = 10; var innerFunc = function(number) { x += number; console.log(x); }; return innerFunc; } ``` x값이 10으로, innerFunc값이 함수 객체로 결정된다. ![image](https://gist.githubusercontent.com/ji3427/308e58e69c5f5385af26efe704813d6d/raw/e43064234cf0e60f0e79dbbeb1dbf33c65ab69f4/SSlide11.jpg) ```javascript function outerFunc() { var x = 10; var innerFunc = function(number) { x += number; console.log(x); }; return innerFunc; } /* * 함수 outerFunc를 호출하면 내부 함수 innerFunc가 반환된다. * 그리고 함수 outerFunc의 실행 컨텍스트는 소멸한다. */ var inner = outerFunc(); ``` outerFunc이 종료되면서 outerFunc의 실행 컨텍스트(EC)는 pop된다. 그리고 전역객체(GO)의 inner값을 return되는 함수 객체로 설정한다. outerFunc의 실행 컨텍스트는 사라지지만 활성객체(AO)는 유지된다. 이 활성객체는 inner값에 할당되어있는 함수 객체의 [[scopes]]를 통해서 접근가능하다. ※ 위와같이 활성객체는 사라지지 않기때문에 innerFunc에서 outerFunc의 x 변수에 접근할 수 있는데 이를 클로저라고 한다. innerFunc 실행 컨텍스트가 생성되고 활성객체를 생성한다. 그리고 innerFunc의 lexical scope가 outerFunc이기 때문에 scope chain은 자신의 활성객체, outerFunc의 활성객체, 전역객체(GO)를 가지는 리스트를 가리킨다. ![image](https://gist.githubusercontent.com/ji3427/308e58e69c5f5385af26efe704813d6d/raw/e43064234cf0e60f0e79dbbeb1dbf33c65ab69f4/SSlide12.jpg) VO가 활성객체(AO)를 가리킨다. innerFunc 내에 변수는 없지만 매개변수가 존재한다. 매개변수는 초기화되면서 값을 설정한다. ![image](https://gist.githubusercontent.com/ji3427/308e58e69c5f5385af26efe704813d6d/raw/e43064234cf0e60f0e79dbbeb1dbf33c65ab69f4/SSlide13.jpg) this가 설정된다. ![image](https://gist.githubusercontent.com/ji3427/308e58e69c5f5385af26efe704813d6d/raw/e43064234cf0e60f0e79dbbeb1dbf33c65ab69f4/SSlide14.jpg) ```javascript inner(5); // 15 /* var innerFunc = function(number) { x += number; console.log(x); }; */ ``` inner 함수를 실행하는데 변수 x는 innerFunc의 활성객체(AO)에서는 찾을 수 없다. 그래서 scope chain을 통해서 상위 scope의 활성객체(AO)에서 값을 찾는다. 상위 scope는 outerFunc의 활성객체(AO)이므로 outerFunc의 x값을 10에서 15로 변경한다. ## 클로저 - 개념 - 클로저는 자신이 생성될 때의 환경(lexical environment)을 기억하는 함수 - 반환된 내부함수가 자신이 선언되었을 때의 렉시컬 환경인 스코프를 기억하여 자신이 선언됐을 때의 환경(스코프) 밖에서 호출되어도 그 환경(스코프)에 접근할 수 있는 함수 - 장점 - 전역변수 사용 억제(상태값 유지) - 정보의 은닉(private 키워드를 흉내낼 수 있음) - 단점 - 활성객체(AO)가 사라지지 않기때문에 메모리 누수가 발생할 수 있다. - scope chain을 타고 올라가기 때문에 속도가 저하될 수 있다. ## let / const ## var와 let,const의 차이점 - var는 함수 레벨 스코프이다. 함수 내에서 선언된 변수는 함수 내에서만 유효하며 함수 외부에서는 참조할 수 없다. - let, const는 블록 레벨 스코프이다. 모든 코드 블록(함수, if 문, for 문, while 문, try/catch 문 등) 내에서 선언된 변수는 코드 블록 내에서만 유효하며 코드 블록 외부에서는 참조할 수 없다. ## 클로저, 호이스팅에서 var와의 다른점 ### 클로저 ```javascript var arr = []; for (var i = 0; i < 5; i++) { arr[i] = function () { return i; }; } for (var j = 0; j < arr.length; j++) { console.log(arr[j]()); } ``` 위의 코드에 경우 4가 5번 출력된다. ```javascript var arr = []; for (let i = 0; i < 5; i++) { arr[i] = function () { return i; }; } for (var j = 0; j < arr.length; j++) { console.log(arr[j]()); } ``` 위의 코드는 0,1,2,3,4가 출력된다. 첫번째 코드에서 var는 함수 레벨의 scope이기 때문에 전역 객체(GO)에 생성된다. arr[i]에 할당되어 있는 함수들은 본인의 scope에 i가 없기때문에 전역 객체(GO)의 scope에서 i를 찾게되고 arr[i]가 실행되는 시점에서 전역 객체(GO)의 i값은 4가 저장되어 있기때문에 모든 arr[i]에서는 4가 출력된다. 두번째 코드에서 let은 블록 레벨의 scope이기 때문에 arr[i]의 각 함수별 활성객체(AO)에 생성된다. arr[i]의 scope에 i값이 존재하기 때문에 0~4까지 순차적으로 값이 나온다. ### 호이스팅 변수는 3단계에 걸쳐 생성됨 1. 선언 단계 - 변수를 실행 컨텍스트의 variable object에 등록 2. 초기화 단계 - variable object에 등록된 변수를 위한 공간을 메모리에 확보. 이 단계에서 변수는 undefined로 초기화됨 3. 할당 단계 - undefined로 초기화된 변수에 실제 값을 할당 var로 선언된 변수는 선언 단계와 초기화 단계가 한번에 이루어짐. 따라서 변수 선언문 이전에 변수에 접근해도 undefined를 반환함. 그러나 let, const로 선언된 변수는 선언 단계와 초기화 단계가 분리되어 진행됨. 따라서 스코프의 시작 지점부터 초기화 시작 지점까지는 변수를 참조할 수 없다. 이 구간을 **일시적 사각지대(Temporal Dead Zone; TDZ)** 라고 부른다. ![image](https://gist.githubusercontent.com/ji3427/308e58e69c5f5385af26efe704813d6d/raw/98c248baf29139e437a87d2a10c6316b13cf01cc/%25EC%2584%25A0%25ED%2583%259D%2520%25EC%2598%2581%25EC%2597%25AD_1541.png) ```javascript // 스코프의 선두에서 선언 단계가 실행된다. // 아직 변수가 초기화(메모리 공간 확보와 undefined로 초기화)되지 않았다. // 따라서 변수 선언문 이전에 변수를 참조할 수 없다. console.log(foo); // ReferenceError: foo is not defined let foo; // 변수 선언문에서 초기화 단계가 실행된다. console.log(foo); // undefined foo = 1; // 할당문에서 할당 단계가 실행된다. console.log(foo); // 1 ``` let, const에서는 호이스팅되지 않는 것처럼 보이지만 그렇지 않다. ```javascript let foo = 1; // 전역 변수 { console.log(foo); // ReferenceError: foo is not defined let foo = 2; // 지역 변수 } ``` 코드 블록 내에서 선언된 변수 foo는 지역 변수이다. 따라서 지역 변수 foo도 해당 스코프에서 호이스팅되고 코드 블록의 선두부터 초기화가 이루어지는 지점까지 일시적 사각지대(TDZ)에 빠진다. 따라서 전역 변수 foo의 값이 출력되지 않고 참조 에러(ReferenceError)가 발생한다.