실행 컨텍스트는 자바스크립트의 동작원리를 담고 있는 개념이다. 스코프, 호이스팅, this, 클로져 등의 개념을 알기 위해 필수로 알아야 한다. 간단히 정리해보자.
그리고 자바스크립트의 실행 컨텍스트는 ES5를 기점으로 구조가 변했다. 먼저 이해가 좀 더 쉬운 ES5이전의 개념을 먼저 보고 현재의 실행 컨텍스트도 살펴보자.
실행 컨텍스트는 코드가 실행되기 위해 필요한 환경이다.
자바스크립트 엔진은 코드를 실행하고 처리하기 위하여 실행에 필요한 여러가지 정보와 환경을 알고 있어야 한다(재료가 있어야 요리를 하겠지?)
그래서 실행 컨텍스트에는 객체의 형태로 된 코드 실행에 필요한 정보(변수 객체, scope chain, this)가 들어있다.
그리고 실행 코드의 종류(전역 코드, 함수 코드, eval 코드)에 따라 실행 컨텍스트의 초기화 과정이 달라지고 실행 컨텍스트의 성격이 결정된다.
생성되는 실행 컨텍스트는 3가지 프로퍼티를 가지고 있다.
Javascript의 실행 컨텍스트들은 스택(stack)의 형태를 구성하고 있고, 다음과 같은 시나리오를 가지고 있다.
예시 코드와 스택을 표현한 그림을 보면 이해가 쉽다.
// 예시
var name = 'aaa';
function bar (newName) {
console.log(`${name}->${newName}`)
}
function foo () {
var name = 'bbb';
bar(name);
}
foo();
이렇게 EC(실행 컨텍스트)들이 쌓이는 것을 볼 수 있는데, 실행 코드의 종류에 따라 global EC(전역 컨텍스트)와 foo EC, bar EC(함수 컨텍스트)로 나뉘어 생성된 것을 알 수 있다.
전역 컨텍스트는 변수객체, scope chain, this
의 정보를 가지고 있다. 변수 객체에는 함수가 아니니 aruguments 정보는 없고 variable 정보만 있다. 호이스팅을 통해 먼저 선언되고 할당은 후에 일어난다. scope chain은 자기 자신인 전역 변수객체만 있을 것이다. this는 따로 설정되어 있지 않으면 window를 가리킨다.
// 예시 코드에서 생성되었을 전역 컨텍스트.
'전역 컨텍스트': {
변수객체: {
arguments: null,
variable: [{name: 'aaa'}, {foo: Function}, {bar: Function}],
},
scopeChain: ['전역 변수객체'],
this: window,
}
함수 컨텍스트도 전역 컨텍스트와 같이 변수객체, scope chain, this
의 정보를 가지고 있다. 근데 변수 객체에는 arguments값이 있으면 넣어준다. 그리고 scope chain은 상위의 전역 변수객체와 해당 함수의 변수객체가 있을것이다. this는 역시 따로 바인딩되어 있지 않으면 window를 가리킨다.
// 예시 코드에서 생성되었을 foo 함수 컨텍스트.
'foo 함수 컨텍스트': {
변수객체: {
arguments: null,
variable: [{name:'bbb'}],
},
scopeChain: ['foo 함수 변수객체', '전역 변수객체'],
this: window,
}
그리고 foo 함수 내부에서 bar 함수가 실행되었으니 그 시점에 bar 함수 컨텍스트도 생성되고 스택에 쌓일 것이다.
// 예시 코드에서 생성되었을 bar 함수 컨텍스트.
'bar 함수 컨텍스트': {
변수객체: {
arguments: [{ newName : 'bbb' }],
variable: null,
},
scopeChain: ['bar 함수 변수객체', 'foo 함수 변수객체', '전역 변수객체'],
this: window,
}
foo 함수에 호출된 console.log 에는 newName과 name 변수가 있는데, newName은 arguments에서 찾을 것이고 name은 scope chain을 따라 전역 변수객체에서 찾을 것이다.
ES5부터의 실행 컨텍스트는 물리적으로 다른 모양의 객체를 갖는다. 변수 객체, 스코프 체인 this의 객체 정보가 LexicalEnvironment로 변경되었다.(this는 ES6부터 LexicalEnvironment에서 담당한다.)
'실행컨텍스트' = {
LexicalEnvironment = [Lexical Environment],
VariableEnvironment = [Lexical Environment],
}
LE는 Environment Record
와 Outer Environment Reference
를 프로퍼티로 갖는다.
앞서 배운 실행 컨텍스트의 물리적인 객체 정보가(변수 객체, 스코프 체인, this객체)가 Environment Record에 저장된다.
'LE' = {
Environment Record: {
변수객체: [Object],
scopeChain: [Object],
},
Outer Environment Reference,
this: [Object]
}
Outer Environment Reference를 통해 중첩된 스코프를 가질 수 있는 환경에서 상위 LE를 참조한다. ES5이전에 스코프체인이 하던 일을 수행하는 것이다. 이 개념을 통해 클로저를 생성할 수 있다.
LE과 동일한 값을 갖는다. 그러나 만들어진 변수 선언 및 함수 선언에 대해 바인딩을 유지한다.
다시 말해 LE는 코드 실행 중에 실행 컨텍스트 내에서 변경될 수 있지만 VE는 항상 값을 유지한다.
그리고 ES6에서 LE와 VE 둘의 차이점은 LE가 함수 선언과 변수 (let, const)의 바인딩을 저장하고 VE는 변수(var)만 저장하는 것이다.
실행 컨텍스트는 ECMA 버전에 따라서 달라지고 앞으로도 바뀔 수 있다. 리서치를 하며 실행 컨텍스트에 대한 정보들이 왜 조금씩 다 달랐는지 이해할 수 있었다. 그러나 그 본질의 개념은 동일하기에 개념만 잘 이해하고 전체적인 흐름을 잘 파악하면 자바스크립트의 공부에 큰 도움이 될 것 같다.