실행 컨텍스트(Execution Context)와 호이스팅(Hoisting)

Hailey Choi
7 min readDec 6, 2020

--

호이스팅을 알기 위해서는 먼저 실행 컨텍스트(Execution Context)가 무엇인지 이해해야 합니다.

실행 컨텍스트(Execution Context)

실행 컨텍스트(Execution Context)는 코드가 실행되는 환경이라고 할 수 있습니다. 실행 컨텍스트는 크게 전역 컨텍스트함수 컨텍스트가 있는데요. 자바스크립트 파일이 실행 되는 순간 단 하나의 전역 컨텍스트가 생성됩니다. 그리고 자바스크립트 코드의 각 함수가 실행될 때 마다 함수 컨텍스트가 생성됩니다.

Eval 함수가 실행되는 Eval 함수 컨텍스트도 존재합니다.

function func1() {
function func2() {
console.log('func2 호출');
}
func2();
console.log('func1 호출');
}
func1();

위의 예제를 통해서 콜 스택에 실행 컨텍스트(Execution Context)가 쌓이는 과정을 그림으로 살펴보겠습니다.

자바스크립트 파일이 로드되면 전역 컨텍스트가 생성되어 콜 스택에 쌓입니다. 코드가 실행되면서 func1 함수를 만나면 func1의 환경 정보를 담은 함수 컨텍스트가 생성되어 콜스택에 쌓입니다. 그리고 func2를 만나면 func2의 함수 컨텍스트가 그 위에 쌓이겠죠.

func2의 코드 실행이 완료되면 func2의 함수 컨텍스트가 콜 스택에서 사라지고 func1의 코드가 마저 실행됩니다. 이후에 func1의 함수 컨텍스트도 콜 스택에서 사라지고 전역 컨텍스트에서의 코드가 마저 실행됩니다. 페이지가 종료되면 전역 컨텍스트도 사라지게 됩니다.

환경 정보

위에서 언급한 환경 정보는 컨텍스트와 관련된 정보를 가지고 있는 객체입니다. 이 객체에는 변수객체, 스코프 체인, this의 세 가지 프로퍼티가 포함되어 있습니다.

'executionContext': {
variableObject: {},
scopeChain: [],
this,
};

위의 같은 모습의 객체입니다. 각각의 프로퍼티에 대해 더 자세히 알아보겠습니다.

변수 객체(Variable Object)

변수 객체는 모든 변수들의 선언 정보를 담는 객체입니다. 이 객체는

  1. 선언된 모든 변수(함수)
  2. 함수의 경우 함수의 인자(Arguments)
  3. 내부 함수

에 대한 정보를 포함합니다.

스코프 체인(Scope Chain)

스코프 체인은 현재 실행 컨텍스트에서 참조할 수 있는 변수 객체들의 리스트입니다. 체인이라는 말에서 알 수 있듯이 자기 자신의 변수 객체를 선두로, 외부 함수(상위 컨텍스트)의 변수 객체, 전역 컨텍스트의 변수 객체 순으로 체인이 연결됩니다.

this

스코프 체인 생성이 완료되면, this에 값이 할당됩니다. this(웹 브라우저에서는 window)는 전역 객체를 가리키고 있다가 함수 컨텍스트가 실행될 때 함수 호출 패턴에 의해 this에 값이 할당됩니다.

전역 컨텍스트의 경우 변수 객체(Variable Object), 스코프 체인(Scope Chain), this 값은 언제나 전역 객체입니다. 전역 객체는 모든 객체들의 최상위 객체를 의미하는데, 일반적으로 웹 브라우저에서는 window 입니다.

호이스팅(Hoisting)

“Before run all the code, the browser first get all declarations , hoisting is the name of this process.” 은 호이스팅을 가장 쉽게 정의하는 말입니다.

즉, 호이스팅이란 코드가 실행되기 전에 브라우저가 모든 변수들의 선언을 먼저 하는 과정입니다. 함수의 선언과 let, const 변수 선언으로 나누어서 살펴보겠습니다.

함수

함수의 경우 함수 표현식은 호이스팅 되지 않지만, 함수 선언식은 통째로 호이스팅됩니다. 코드를 통해 비교해 보겠습니다.

  1. 함수 표현식
console.log(expression); // undefined
expression(); // 에러 발생

// 함수 표현식 (호이스팅 피하기)
var expression = function () {
console.log("expression");
};

console.log(expression)이라는 코드가 실행되는 시점에서 보면 expression 이라는 변수의 선언 자체는 호이스팅되었지만 이 변수에 함수가 할당되는 코드는 실행되지 않았습니다. 그래서 undefined 가 되죠. 그리고 expression 함수를 실행시켰을 경우 expression이 함수가 아니라는 에러가 발생합니다.

2. 함수 선언식

console.log(expression); // function declaration
declaration(); // "declaration"

// 함수 선언식 - 선언과 동시에 초기화
function declaration() {
console.log("declaration");
}

함수 선언식에서는 선언과 동시에 초기화가 이루어지기 때문에 호이스팅이 일어납니다. console.log(expression) 코드를 실행했을 때 expression은 함수를 뜻하고, 함수를 실행 했을 때 정상적으로 작동하는 것을 알 수 있습니다.

let, const

let, const또한 호이스팅 됩니다. 그런데 유의해야 할 점이 있습니다. 예제를 살펴보죠.

console.log(str); // ReferenceError

const str = "apple";

분명히 let, const 변수 또한 호이스팅 된다고 했는데 ReferenceError가 발생했습니다. 어떻게 된 것일까요? let, const 키워드로 선언된 변수는 TDZ(Temporal Dead Zone)의 영향을 받기 때문입니다.

TDZ(Temporal Dead Zone) ?

TDZ 시맨틱은 선언 전에 변수에 접근하는 것을 금지합니다.

위의 예제에서 변수 str은 호이스팅 되었지만const 키워드로 선언되었기 때문에 TDZ에 머물게 되어 접근이 불가능하고 이에 따라 오류가 발생합니다. 그럼 언제 변수에 접근이 가능할까요? 바로 코드 실행 중 변수가 선언되는 지점입니다. 이 때 TDZ에서 벗어나게 되어 접근이 가능해지죠.

실행 컨텍스트가 생성될 때 let/const 변수 선언은 Lexical Environment에 저장됩니다. Lexical Environment에 저장된 변수들은 초기화(값 바인딩)되기 전까지는 접근할 수 없는 TDZ 상태에 있게 됩니다. 반면에 var 변수 선언은 Variable Environment에 따로 저장되며, TDZ에 영향을 받지 않습니다.

여기서 변수가 초기화 된다는 것은 변수에 값이 할당될 때가 아닌, 변수가 선언될 때를 의미합니다.

let tmp;
console.log(tmp); // undefined

tmp = 10;
console.log(tmp); // 10

변수 tmp에 값이 할당되지 않았지만, 선언되었기 때문에 tmp는 TDZ에 있지 않습니다. 이때 변수 tmp에는 undefined가 초기화(바인딩)되어 있습니다.

참고

--

--