Javascript Hoisting

JavaScript Hoisting(호이스팅) 이란?

호이스팅은 끌어올리다라는 뜻인데, 풀이하자면 후선언된 변수나 함수들이 해당 Scope에서 최상위로 끌어 올려지는 걸 말한다.
자바스크립트엔진은 해당 실행 문맥(Execution Context)의 생성시, 즉 Runtime 시점에서 변수 선언문이나 함수 선언문을 읽기 전에 선언된 변수와 함수들을 다른 무엇보다도 먼저 읽어 Scope(유효범위)의 최상위로 끌어올려 위치시킨다.
이 덕에, 훨씬 뒤에서 선언된 함수들과 변수들을 그 전에 사용이 가능하다.
참고로 변수의 경우에는 변수 선언(Variable Declaration)만 Hoisting 된다, 즉 Variable Initialization이 있다면 변수가 선언은 되나, 변수에 어떤 값도 들어가지 않는다는 뜻.
또한 함수의 실행 문맥(Execution Context)의 생성은 함수 호출시 이뤄지므로, 함수 내부에서 선언된 변수들은 함수 호출시에서야 Hoisting 된다.

예제를 통해 살펴보기로 하자.

1
2
3
4
5
6
7
8
9
10
11
12
console.log(asdf); // undefined, 오류가 나질 않는다. 즉 프로그램이 뻗질 않는다.
console.log(qwer); // undefined
console.log(zxcv); // function zxcv() { console.log(456); }

var asdf = 'asdf';
var qwer = function() {
console.log(111);
};

function zxcv() {
console.log(456);
}

전통적인 프로그래밍 언어를 방식으로 다루는 개발자들은 이해하지 못하는 부분 중 하나가 바로 이 부분일 것이다.
선언하기 전에 사용된 변수, 즉 상단에서 선언된적이 없는 변수인데 어디서 갑자기 나타나서 undefined를 콘솔에 출력하는데 프로그램이 왜 죽지 않는 것일까?
그 이유는 바로 호이스팅이 일어났기 때문이다.

위의 코드는 호이스틩으로 인해 아래와 같이 변환되어 브라우저에 렌더링된다고 보면 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var asdf;
var qwer;

function zxcv() {
console.log(456);
}

console.log(asdf);
console.log(qwer);
console.log(zxcv);
asdf = 'asdf';

qwer = function() {
console.log(111);
};

함수 선언문에서는 변수 없이 함수를 선언한 것이므로 전체 코드가 호이스팅 된다.
하지만 함수 표현식은 변수 선언과 할당을 구분할 수 있으므로 선언 부분만 호이스팅이 일어나게 된다.
따라서 오류가 날 것으로 예상했던 코드가 예측한 대로 작동하질 않는데 호이스팅에 대한 개념이 없다면 이런 경우가 혼란스럽게 하기 마련이다.
따라서 사용할 변수와 함수는 최소한 사용하기 전에 미리 선언하는 방식을 지향하는것이 안티패턴을 줄이고 개발자 의도대로 동작한다고 확신을 가질수 있는 좋은 코드 패턴일 것이다.

ES2015(ES6) 호이스팅

ES2015+에서는 호이스팅이 어떻게 처리되는지 알아보자.
일단 ES2015+에서는 변수를 선언할 때 var 대신 const와 let으로 사용한다.

1
2
3
4
console.log(asdf); // Uncaught ReferenceError: asdf is not defined
console.log(qwer);
const asdf = 'asdf';
let qwer = 'qwer';

1번 라인에서 오류가 나서 2번 라인은 실행도 못하고 바로 뻗어버린다.
개발자의 바람대로 호이스팅이 사라진 것 같아 보인다.
과연 정말 그런것일까…?

TDZ(Temporal Dead Zone)

임시적 사각 지대, ‘변수가 임시로 죽어있는 공간’ 이라고 이해하면 될 것 같다.
예제를 통해 살펴보자.

1
2
3
4
5
6
const asdf = 'asdf';
const qwer = function() {
console.log(asdf); // Uncaught ReferenceError: asdf is not defined
let asdf = 'qwer';
}
qwer();

호이스팅이 일어나지 않았다면 3번 라인에서는 ‘asdf’가 찍혀야 정상일 것이다.
혹은 호이스팅이 일어났다면 undefined라도 찍혀야하는데 오히려 에러를 출력한다.
이런 경우가 바로 TDZ 때문인데 위 코드를 다시 TDZ라는 것과 연관 지어서 풀어보면 아래와 같다.

1
2
3
4
5
6
7
8
9
const asdf = 'asdf';
const qwer = function() {
let asdf; // 사실 이렇게 변수 선언부가 호이스팅에 의해 스코프 상단으로 끌어올려진다.
// 변수의 초기화 구문을 만나기 전까지 TDZ가 형성됨.
console.log(asdf); // TDZ에서 해당 변수가 쓰였다면 에러를 발생!
// 여기까지 TDZ.
asdf = 'qwer';
}
qwer();

TDZ는 변수 선언(호이스팅에 의해 스코프 상단으로 끌어올려진 부분)부터 변수의 할당을 만나기 전 부분까지 형성이 되는 구간인데 이 TDZ에서는 해당 변수가 임시적으로 죽어있는 구역인데 이 구역에 해당 변수가 끼어들면서 오류가 발생하게 되는 것이다.

참조

공유하기