코드를 실행하려면 환경이 필요하다
var x = 10이라는 코드를 실행하려면, 엔진은 몇 가지를 알아야 합니다.
x라는 변수를 어디에 저장할 것인가?- 이 코드의
this는 무엇을 가리키는가? - 바깥 스코프에 있는 변수에 어떻게 접근할 것인가?
이 정보를 담고 있는 것이 실행 컨텍스트 (Execution Context) 입니다. JavaScript 엔진은 코드를 실행하기 전에 반드시 실행 컨텍스트를 먼저 생성합니다.
실행 컨텍스트의 종류
JavaScript에는 세 종류의 실행 컨텍스트가 있습니다.
- 전역 실행 컨텍스트, 스크립트가 시작될 때 딱 하나 생성.
window(브라우저) 또는globalThis(Node.js, 구global) 객체와 연결 - 함수 실행 컨텍스트, 함수가 호출될 때마다 생성. 같은 함수를 10번 호출하면 10개의 컨텍스트가 생성됨
- eval 실행 컨텍스트,
eval()호출 시 생성 (거의 쓰이지 않음)
실행 컨텍스트의 두 단계
각 실행 컨텍스트는 생성 단계와 실행 단계를 거칩니다.
생성 단계 (Creation Phase)
코드를 실행하기 전에 일어납니다.
this바인딩 결정- 변수/함수 선언을 찾아서 메모리에 등록
var변수 →undefined로 초기화- 함수 선언 → 함수 객체로 초기화 (전체가 메모리에 올라감)
let/const→ 등록되지만 초기화되지 않음 (TDZ)
- 외부 환경 참조 설정 (스코프 체인)
이것이 바로 호이스팅의 실체입니다. 코드가 "끌어올려지는" 것이 아니라, 생성 단계에서 선언이 먼저 처리되는 것입니다.
실행 단계 (Execution Phase)
코드를 한 줄씩 실행합니다. 변수에 값을 할당하고, 함수를 호출하고, 표현식을 평가합니다.
콜스택과 실행 컨텍스트
함수가 호출되면 새 실행 컨텍스트가 콜스택에 push되고, 함수가 반환되면 pop됩니다. 아래 시각화에서 중첩 함수 호출 시 콜스택이 어떻게 변하는지 확인하세요.
핵심 관찰:
- 생성 단계에서
var y가undefined로 초기화된 것을 볼 수 있습니다. 이것이 호이스팅입니다 - 함수 선언 (
bar) 은 생성 단계에서 바로 함수 객체로 초기화됩니다 bar()안에서x를 찾을 때, 자신의 컨텍스트 → foo → Global 순서로 스코프 체인을 탐색합니다- 함수가 반환되면 컨텍스트가 pop됩니다. LIFO 순서입니다
실행 컨텍스트의 내부 구조
ECMAScript 명세에 따르면 실행 컨텍스트는 이런 구성 요소를 가집니다.
LexicalEnvironment
let, const, 함수 선언이 저장되는 환경. 환경 레코드 (Environment Record) 와 외부 환경 참조 (Outer Reference) 로 구성됩니다.
VariableEnvironment
var 변수가 저장되는 환경. 함수/전역 수준에서는 LexicalEnvironment와 같은 객체를 가리킵니다. var 선언은 선언 인스턴스화 단계에서 가장 가까운 함수/전역/모듈의 VariableEnvironment에 등록되므로, 중간에 어떤 블록이 있어도 블록에 묶이지 않고 함수 스코프에 귀속됩니다. 반면 let/const는 블록 진입 시 새로 만들어지는 LexicalEnvironment에 바인딩되어 블록 스코프를 가집니다.
ThisBinding
현재 컨텍스트의 this 값. 클래식 스크립트의 전역에서는 전역 객체 (window) 를 가리키지만, ES 모듈의 최상위에서는 undefined입니다. 메서드 호출에서는 호출 객체, new에서는 새 인스턴스를 가리킵니다. (5편에서 자세히 다룹니다)
호이스팅 다시 보기
실행 컨텍스트를 이해하면 호이스팅이 더 이상 마법이 아닙니다.
console.log(a); // undefined (var, 생성 단계에서 undefined로 초기화)
console.log(b); // ReferenceError (let, 생성 단계에서 등록만, 초기화 안 됨)
console.log(foo); // function (함수 선언, 생성 단계에서 전체가 초기화)
var a = 1;
let b = 2;
function foo() {}var a, 생성 단계에서undefined로 초기화 → 접근 가능하지만 값은undefinedlet b, 생성 단계에서 등록만 → 초기화 전 접근 시 ReferenceError (TDZ)function foo, 생성 단계에서 함수 객체로 초기화 → 선언 전에도 호출 가능
정리
실행 컨텍스트는 JavaScript 실행의 기본 단위입니다.
- 코드가 실행되려면 반드시 실행 컨텍스트가 필요하다
- 전역 컨텍스트는 하나, 함수 컨텍스트는 호출마다 생성
- 생성 단계에서 변수/함수를 등록하고 (→ 호이스팅), 실행 단계에서 코드를 수행
- 콜스택이 실행 컨텍스트의 생명주기를 관리
다음 단계
실행 컨텍스트가 "외부 환경 참조"를 통해 바깥 변수에 접근한다고 했습니다. 이 참조가 어떻게 연결되고, 변수를 찾을 때 어떤 순서로 탐색하는지, 다음 글에서 스코프와 스코프 체인을 자세히 살펴보겠습니다.