호이스팅(Hoisting)
자바스크립트에서 호이스팅이란 인터프리터가 코드를 실행하기 전에 함수, 변수, 클래스 또는 import 선언문을 해당 범위의 맨 위로 끌어올리는 것처럼 보이는 현상을 의미한다.
개발자가 어느 라인 위치에 코드를 선언해도 실행되기 전 코드가 최상단으로 끌어올려지고 실행되게 된다.
이러한 호이스팅이 발생하는 원인은 자바스크립트의 변수 생성과 초기화의 작업이 분리돼서 진행되기 때문이다.
그럼 호이스팅이 어떤 상황에서 발생하는지 알아보자.
호이스팅이 발생되는 상황
- 변수의 호이스팅
간단한 예제를 통해서 호이스팅이 발생하는 상황을 살펴보자.
// 이렇게 코드를 작성하면 1번 코드에서 undefined가 출력된다.
console.log(name) // 1
var name = 'test'
console.log(name) // 2
위의 예제 코드와 같이 첫번째 라인에서 name을 출력하고 있는 것을 확인할 수 있지만 자세히 생각해 보면 name이라는 변수는 아직 생성되지 않은 변수인데 첫 번째 라인에서 console.log를 통해 undefined가 출력된다.
var name
console.log(name)
name = 'test'
console.log(name)
호이스팅이 발생하는 코드를 자세히 풀어보면 위의 코드와 같이 변수의 선언과 초기화 작업이 분리돼서 실행된다.
먼저 var name이 호이스팅되어 맨 위의 라인으로 끌어올려져 실행된다. 그 후 console.log(name)을 하면 undefined가 출력되고 그다음으로 name에 test라는 값을 저장한 뒤 다시 출력해 보면 그제야 test가 출력된다.
예제를 통해 알 수 있는 것은 변수가 선언된 라인 이전에 해당 범위에서 변수 값을 사용하는 경우 호이스팅이 된다는 것을 알 수 있다.
또한, 변수가 선언된 라인 이전에 해당 범위의 변수를 참조할 수 있지만 신기하게도 ReferenceError를 던지지 않고 값이 항상 undefined이 출력된다.
이러한 변수 호이스팅은 var 키워드를 사용했을 때 발생할 수 있다.
그렇다면 es6로 넘어오면서 새롭게 생긴 let과 const 키워드는 호이스팅이 안되는 것일까?
console.log(name)
let name = 'test'
console.log(name)
앞서 진행했던 var 키워드를 사용한 호이스팅 예제와 똑같이 작성한 예제로 키워드만 let으로 변경해 주었다.
위의 코드를 실행하면 var 키워드와 마찬가지로 호이스팅이 되는 것을 알 수 있다.
하지만 출력 결과에 대해서 var 키워드와 큰 차이점을 가지는데 바로 undefined가 아닌 아래와 같은 ReferenceError를 출력한다는 점이다.
ReferenceError: Cannot access 'name' before initialization
여기서 헷갈렸던 부분이 있었는데 let과 const 키워드가 호이스팅이 된다는 것을 어떻게 알 수 있는지였다.
그에 대한 해답은 let과 const를 사용하여 출력되는 에러를 통해서 알 수 있었다.
이전에 var 키워드는 undefined를 통해서 호이스팅이 발생했다는 것을 알았다. 위의 ReferenceError도 자세히 보면 name이라는 변수가 초기화되지 않았다는 것을 알려주는데 즉, name은 초기화가 되지 않은 undefined 상태라는 것을 알 수 있다.
변수의 호이스팅을 공부해 보면 왜 개발자들이 let과 const 키워드를 권장하는지 알 수 있다.
해당 키워드를 사용함으로써 호이스팅은 되지만 초기화를 하기 전까지 변수에 접근하면 에러를 발생하여 예상치 못한 동작을 일으킬 수 없도록 막을 수 있다.
- 함수의 호이스팅
호이스팅은 변수 외에도 함수에서도 발생할 수 있다.
기본적으로 함수를 정의하는 문법으로는 함수 표현식과 함수 선언식이 있다.
먼저 함수 선언식을 살펴보자.
console.log(add(2, 3));
function add(x, y) {
return x + y;
}
console.log(add(2, 3));
위의 예제 코드와 같이 함수 선언식을 작성하여 실행해 보면 호이스팅이 되는 것을 확인할 수 있다.
첫 번째 라인에서 add(2, 3)은 아직 add라는 함수가 정의되지 않았음에도 불구하고 add 함수를 호출하여 연산을 진행한 결과인 5를 출력해 준다.
// 함수 선언식 호이스팅
function add(x, y) {
return x + y;
}
console.log(add(2, 3));
console.log(add(2, 3));
이것이 가능한 이유는 위의 코드와 같이 호이스팅으로 인해 add 함수가 최상단으로 끌어올려졌기 때문이다.
console.log(result(2, 3));
const result = function (x, y) {
return x + y;
};
console.log(result(2, 3));
다음으로 함수 표현식을 살펴보면 위의 코드와 같다. 말 그대로 함수를 정의해 변수에 저장하여 표현해 주는 것이다.
위와 같이 함수 표현식 형태로 함수를 정의하게 되면 함수 선언식과 다르게 첫 번째 라인의 console.log(add(2,3)) 코드에서 ReferenceError: Cannot access 'result' before initialization 에러가 발생하게 된다.
함수를 호출한다는 점만 보면 함수 선언식을 사용해도 이상할 게 없다는 생각이 든다. 하지만 호이스팅이 된다는 것만으로도 코드의 구조가 예상치 못하게 동작할 수 있는 가능성을 열어두는 것 같아 되도록 함수 표현식을 사용하는 것이 좋을 것 같다고 생각한다.
참고 자료