호이스팅


호이스팅이란 자바스크립트에서 변수 또는 함수의 선언이 스코프의 최상단으로 끌어 올려지는 현상입니다. 이 글에서는 호이스팅에 대해 깊게 다뤄볼 예정입니다.

함수 호이스팅

자바스크립트에서는 함수가 스코프 내에서 정의되어 있으면 해당 함수가 선언된 위치와 관계없이 해당 함수를 사용할 수 있습니다.

foo();

function foo() {
    // Do something
}

위 코드에서 foo는 선언되기 이전에 호출되었지만 아무런 에러 없이 동작합니다. 이는 함수의 선언이 호이스팅되었기 때문입니다. 하지만 함수를 함수 표현식, 또는 화살표 함수를 이용해 함수를 생성하고 변수에 할당하는 경우에는 호이스팅이 일어나지 않습니다.

foo();		// 에러 발생

var foo = function() {
  console.log('foo')
}

변수 호이스팅

변수 호이스팅은 var을 통해 선언된 변수와 let 또는 const를 통해 선언된 변수가 서로 다르게 동작합니다.

var

var을 통해 선언된 변수는 변수의 선언이 호이스팅되어 변수에 값을 할당하기 전까지는 undefined를 기본값으로 갖고 있습니다. 따라서 아래와 같이 코드를 작성했을 때 변수 선언 전에 변수를 참조해도 에러가 발생하지 않습니다.

console.log(x);		// undefined
var x = 10;
console.log(x);		// 10

let, const

반면에 letconst로 선언한 변수는 호이스팅이 일어나는 것은 동일하지만 변수 선언 이전에 변수를 참조하면 ReferenceError를 발생시킵니다. 즉 위와 동일한 코드를 let을 사용해 실행하면 에러가 발생합니다.

console.log(x);		// Reference Error
let x = 10;
console.log(x);

그리고 이 때 스코프가 시작된 구간부터 변수가 선언되기 이전까지, 즉 변수를 참조했을 때 ReferenceError를 발생시키는 구간을 Temporal Dead Zone 또는 그냥 Dead Zone이라고 합니다. 이는 var을 사용했을 때는 존재하지 않는 ES6에서 새롭게 생긴 개념입니다.

원리

앞에서 설명은 함수 또는 변수의 선언이 스코프 최상단으로 끌어 올려지는 현상이라고 했지만 실제 원리가 코드에서 선언부를 분리시켜 스코프의 최상단으로 옮기는 것은 아닙니다.

자바스크립트는 코드를 실행할 때 실행 컨텍스트(Execution Context)라는 것을 생성하는데, 이 과정에서 코드에 선언된 변수와 함수를 위한 메모리 공간을 확보합니다. 그리고 변수는 undefined로 초기화시키고 함수는 함수의 몸체(body) 전체를 저장합니다. letconst도 마찬가지로 메모리에 undefined로 초기화되지만 둘은 letconst를 위한 별도의 공간에 저장되고 초기화 이전에 참조가 일어나게 되면 ReferenceError를 발생시킵니다. 즉 letconstvar과 마찬가지로 호이스팅 자체는 일어나지만 초기화 이전의 참조에 대해 var과는 다른 동작을 하는 것 뿐입니다.

이를 통해 함수 표현식이나 화살표 함수를 이용한 함수는 호이스팅이 일어나지 않는 것 처럼 보이는 이유도 알 수 있습니다. 해당 방식으로 함수를 만들면 함수 선언이 아닌 변수 선언으로 인식을 하고 undefined로 초기화되기 때문에 undefined를 함수로 호출하려 해서 에러가 발생하는 것입니다. 실제로 이 때 발생하는 에러는 foo is not a function과 같이 ReferenceError가 아닌 것을 알 수 있습니다(물론 let이나 const를 사용하면 ReferenceError가 발생합니다).

foo()

var foo = function () {
  console.log('foo')
}