이전 포스트에서 실행 컨텍스트에 대해 정리한 적이 있다. 실행 컨텍스트는 스택 구조를 기반으로 작동하며, 콜 스택의 맨 위에 쌓인 실행 컨텍스트가 현재 실행 중인 코드에 관여하게 된다. 이는 새로 쌓인 컨텍스트가 기존의 컨텍스트 위에 위치하기 때문에 가능한 구조다.
어떤 실행 컨텍스트가 활성화되면, 자바스크립트 엔진은 해당 컨텍스트에서 코드를 실행하기 위해 필요한 환경 정보들을 수집하여 실행 컨텍스트 객체에 저장한다. 이 객체는 엔진이 활용하기 위한 용도로 생성되며, 개발자가 이를 직접 확인할 수는 없다. 따라서 실행 컨텍스트의 구조를 이해하는 것이 자바스크립트의 동작 방식을 깊이 파악하는 데 매우 중요하다.
실행 컨텍스트는 자바스크립트 코드가 실행되는 환경을 구성하는 핵심 요소이다. 이를 통해 변수, 함수, 그리고 this
의 동작 방식을 이해할 수 있다.
VariableEnvironment
는 실행 컨텍스트가 처음 생성될 때, 해당 컨텍스트에서 사용할 변수와 함수 선언에 대한 초기 스냅샷을 저장한다. 이후 LexicalEnvironment
를 주로 사용하지만, VariableEnvironment
는 최초 실행 시점의 상태를 유지하며, LexicalEnvironment
는 그 후에 변경되는 동적인 값을 반영한다.
LexicalEnvironment
는 실행 컨텍스트가 실행 중일 때, 코드가 참조하는 변수 들과 스코프 체인 에 대한 정보를 관리한다. 이를 통해 현재 컨텍스트와 외부 컨텍스트 간의 연결이 이루어진다.
this
는 함수가 호출될 때 어떤 객체를 가리킬지를 결정하는 중요한 요소이다. 함수 호출 방식에 따라 this
가 참조하는 객체가 달라지며, 이에 따라 함수 내에서 this
의 값이 다르게 바인딩된다.
그렇다면 실행 컨텍스트의 구성 요소들이 실제 코드에서 어떻게 동작하는지 알아보려고 한다.
function outer() {
const a = 1;
function inner() {
const b = 2;
console.log(a + b); // 3
}
inner();
}
outer();
동작 과정을 보면,
outer
함수가 호출되면 새로운 실행 컨텍스트가 생성된다.a: 1
을 포함한다.inner
함수가 호출되면 또 다른 실행 컨텍스트가 생성된다.inner
의 렉시컬 환경에는 b: 2
가 저장된다.inner
의 Outer Environment Reference는 outer
의 렉시컬 환경을 참조한다.console.log(a + b)
에서 a
를 찾을 때, inner
의 렉시컬 환경에서 없으므로 스코프 체인을 따라 outer
의 렉시컬 환경에서 검색한다.VariableEnvironment
에 저장되며, 이후 LexicalEnvironment
에서 실행된다.console.log(foo); // undefined
var foo = 10;
console.log(bar); // ReferenceError
let bar = 20;
baz(); // "Hello"
function baz() {
console.log("Hello");
}
호이스팅이 일어나고 실제 코드 실행은 어떻게 동작하는지 예제 코드를 통해 확인해보자,
var foo
는 선언만 되어 undefined
로 초기화된다.let bar
는 TDZ(Temporal Deep Zone)에 놓여 초기화되지 않는다.function baz
는 전체 함수 선언이 호이스팅 된다.console.log(foo)
는 foo
의 초기 값 undefined
를 출력한다.console.log(bar)
는 let
선언된 bar
가 TDZ 상태에 있어 ReferenceError를 발생시킨다.baz()
는 함수 선언이 호이스팅되었으므로 호출이 가능한 것이다.this
의 값을 동적으로 결정한다.const obj = {
value: 100,
method() {
console.log(this.value); // 100
},
};
function globalFunc() {
console.log(this); // window (브라우저) 또는 global (Node.js)
}
obj.method();
globalFunc();
obj.method()
에서 this
는 메서드를 호출한 객체 obj
를 바인딩한다.this.value
는 100
을 출력한다.globalFunc()
에서 this
는 전역 객체를 바인딩한다.window
, Node.js에서는 global
이 출력된다.여기서 핵심은 자바스크립트 엔진이 실제로 끌어올리지는 않지만 변수 정보를 수집하는 과정을 이해하기 쉬운 방법으로 호이스팅이라는 대체 가상의 개념을 만든 것이다. 그리고 함수는 함수 전체가 최상단으로 끌어올려진다고 했는데 함수 선언부 전체가 코드의 최상단으로 이동한다는 것을 의미하고, 이는 함수 이름과 함수 본체가 모두 호이스팅된다는 의미이다.
그렇기 때문에 함수가 정의된 위치에 상관없이 코드 어디에서든 해당 함수를 호출할 수 있게 된다. 이 부분에서 함수 선언과 함수 표현식이 다르게 호이스팅된다고 하는데 다음 장에 정리해보려고 한다.