실행 컨텍스트는 scope, hoisting, this, function, closure
등의 동작 원리를 담고 있는 자바스크립트의 핵심 원리이다.
실행 컨텍스트는 실행 가능한 코드가 실행되기 위해 필요한 환경이며 실행 가능한 코드는 아래와 같다.
자바스크립트는 엔진이 코드를 실행하기 위해서 많은 정보를 알고 있어야 한다. 자바스크립트 엔진은 아래의 정보들을 형상화하고 구분하기 위해서 실행 컨텍스트를 물리적 객체로 관리한다.
실행 컨텍스트는 객체의 형태를 가지며 3개의 프로퍼티가 존재한다.
해당 객체는 아래의 정보를 담는 객체이다.
VO는 다른 객체를 가르키는데 전역 코드 실행 시 생성되는 전역 컨텍스트와 함수 호출 시 생성되는 함수 컨텍스트는 가르키는 객체가 다르다.
이유는 내용이 다르기 때문인데 전역 함수는 매개 변수가 없지만 함수 컨텍스트는 매개 변수가 존재한다.
전역 컨텍스트의 VO는 전역 변수와 전역 함수 등을 포함하는 젼역 객체(Global Object / GO)를 가르킨다.
함수 컨텍스트의 VO는 매개 변수와 인수, 지역 변수, 내부 함수를 포함하는 활성 객체(Activation Object / AO)를 가르킨다.
스코프 체인은 전역 또는 함수가 참조할 수 있는 변수, 함수 선언 등의 정보를 담고 있는 전역 객체(GO)와 활성 객체(AO)의 리스트를 가진다.
현재 활성화 되어있는 실행 컨텍스트의 활성 객체(AO)를 선두로 순차적으로 상위 컨텍스트의 활성 객체(AO)를 가르키며 마지막 리스트는 전역 객체(GO)를 가르킨다.
함수가 중첩 상태일 때 하위 함수 내에서 상위 함수의 스코프와 전역 스코프까지 참조할 수 있는데 이것은 스코프 체인 검색을 통해서 가능하다.
함수 실행 중 변수를 만나면 그 변수를 우선 현재 Scope, 즉 활성 객체(AO)에서 검색해보고 실패하면 스코프 체인에 있는 순서대로 그 검색을 이어나간다.
전역 객체(GO)까지 검색해서 실패하면 정의되지 않은 변수에 접근하는 것으로 판단하고 Reference 에러가 발생한다.
this 프로퍼티는 this 값이 할당되며, this는 함수 호출 패턴에 따라서 동적으로 결정된다.
this의 자세한 내용은 해당 링크를 참조하세요.
아래의 에제 코드를 가지고 실행 컨텍스트가 어떻게 생성되는지 알아보자.
var x = 'xxx';
function foo () {
var y = 'yyy';
function bar () {
var z = 'zzz';
console.log(x + y + z);
}
bar();
}
foo();
컨트롤이 실행 컨텍스트로 진입하기 이전에 전역 객체(GO)가 생성된다. 초기 전역 객체는 빌트인 객체(Math 등)와 BOM, DOM이 설정되어 있다.
전역 객체(GO) 생성 후 전역 코드로 컨트롤이 진입하면 전역 컨텍스트가 생성되고 실행 컨텍스트 스택에 쌓인다.
그리고 아래의 순서대로 처리가 이뤄진다.
컨텍스트가 생성된 후 가장 먼저 스코프 체인의 생성과 초기화가 실행된다. 스코프 체인은 전역 객체(GO)를 포함하는 리스트가 된다.
변수 객체화는 변수 객체(VO)에 프로퍼티와 값을 추가하는 것을 의미한다. 변수, 매개 변수와 인수 정보, 함수 선언을 변수 객체(VO)에 추가하여 객체화한다.
변수 객체화는 아래의 순서대로 변수 객체(VO)에 프로퍼티와 값을 할당한다.
함수 호이스팅
변수 호이스팅
위 예제 코드를 보면 변수 x와 함수 foo가 선언되었다. 변수 객체화 순서에 따르면 우선 함수 foo의 선언이 처리되고 변수 x가 처리된다.
전역에 선언된 함수 foo는 전역 실행 컨텍스트의 변수 객체(VO)가 가르키는 전역 객체(GO)의 프로퍼티로 함수 이름인 foo가 값으로는 생성된 함수 객체가 설정된다.
생성된 함수 객체는 [[scope]] 프로퍼티를 가지며 [[scope]]는 함수 객체만 소유하는 내부 프로퍼티이다.
[[scope]] 프로퍼티는 현재 실행 컨텍스트의 스코프 체인이 참조하고 있는 객체를 가르키며 자신을 포함하는 외부 함수의 실행 환경과 전역 객체를 가르킨다. 자신을 포함하는 외부 함수의 실행 컨텍스트가 소멸해도 [[scope]] 프로퍼티가 가르키는 외부 함수의 실행 환경은 소멸하지 않고 참조할 수 있다. 이것이 클로저이다.
아직 코드가 실행되기 이전이며 스코프 체인이 가르키는 변수 객체(VO)에 이미 함수가 등록되어 있으므로 함수 선언문 이전에 함수 호출이 가능한 것이다. 이것을 함수 호이스팅
이라고 부른다.
변수 선언은 아래와 같이 3가지 단계로 처리한다.
undefined
로 초기화한다.undefined
로 초기화된 변수에 실제 값을 할당한다.var
키워드로 선언된 변수는 선언 단계와 초기화 단계가 한번에 이뤄진다. 변수 선언문 이전에 변수를 참조해도 실행 컨텍스트의 변수 객체(VO)에는 참조한 변수가 undefined
로 초기화가 되어있기 때문에 에러가 발생하지 않고 undefined
를 반환한다. 이것을 변수 호이스팅
이라고 부른다.
변수 선언 처리를 끝낸 후 this value를 결정한다. this value는 전역 객체를 가르키고 있다가 함수 호출 패턴에 의해서 this에 할당되는 값이 결정된다.
전역 코드의 경우에 this는 전역 객체를 가르킨다.
전역 코드 실행을 위한 준비는 끝났고 지금부터 코드가 실행된다.
var x = 'xxx';
function foo () {
var y = 'yyy';
function bar () {
var z = 'zzz';
console.log(x + y + z);
}
bar();
}
foo();
전역 변수 x의 값을 ‘xxx’로 할당할 때 현재 실행 컨텍스트의 스코프 체인이 참조하고 있는 활성 객체(VO)를 선두부터 검색하여 변수명에 해당하는 프로퍼티가 발견되면 값을 ‘xxx’로 할당한다.
함수 foo가 호출되면 컨트롤이 이동하고 실행 컨텍스트가 생성되며 아래의 과정이 순차적으로 진행된다.
함수 bar가 호출되면 컨트롤이 이동하고 실행 컨텍스트가 생성되며 아래의 과정이 순차적으로 진행된다.
console.log(x+y+z);
위 코드의 결과 값은 xxxyyyzzz
가 된다.