JS - Hoisting
둘리가 var 변수에 값 할당전에 해당 변수를 출력하는 모습이다.
(undefined : 정신이 들어? )
ㅤ
ㅤ
우선 호이스팅은 변수 호이스팅과 함수 호이스팅이 있다.
먼저, 변수 호이스팅부터 보도록 하자.
변수 호이스팅
일단, 호이스팅이 무엇인지 알고 가야한다. 호이스팅을 MDN에서는 이렇게 설명하고 있다.
💡 JavaScript에서 호이스팅(hoisting)이란, 인터프리터가 변수와 함수의 메모리 공간을 선언 전에 미리 할당하는 것을 의미합니다.
인터프리터, 메모리공간, 선언, 할당?? 당장 글만 보니 이게 무엇인지 이해하기 힘들다.
보통 이럴 상황에는 코드를 보면 빠른 이해가 가능하다. 아래에 둘리가 짠 코드를 보고 어떤 값이 출력될지 맞춰보자.
console.log(cosmos); // output : ??
cosmos = "ho-it";
console.log(cosmos); // output : ??
var cosmos;
자유도가 높다는 파이썬도 오류가 이렇게 짜면 오류가 난다.
필자도 처음 이 코드를 봤을 때의 의식의 흐름은 이랬다.
- 대체 이런 코드가 왜 존재하지?
- 이걸 실행시키면 첫 출력문에서 오류가 나겠는데?
- 오케이 그럼 에러가 발생 하겠는걸?
하지만, 놀랍게도 위의 코드는 아무런 문제없이 동작한다. 답은 , undefined, “ho-it”이다. 대체 왜 그런 것일까?
⇒ 이는 호이스팅이 발생했기 때문인데 해당 코드의 실행 순서를 나열해보겠다.
- 먼저 코드가 실행 전, JS 엔진에 의해 var cosmos는 선언+초기화 된다.
- 초기화가 된 cosmos는 undefined 값을 가지게 된다.
- 이 상태에서 코드가 실행된다. 첫 출력문은 그렇기 때문에 undefined를 출력한다.
- 두 번째 줄에서 cosmos에 “ho-it”이 할당된다.
- 두 번째 출력문이 “ho-it”을 출력한다.
대략 이 순서로 이 코드는 실행됬다.
여기서 보면, 마치 “var cosmos”의 변수 선언문이 스코프의 맨 위로 끌어 올려진 것 처럼 보이는데 이런 현상을 호이스팅(hoisting)이라고 한다. ㅤ ㅤ
?? : 그럼 이런 현상이 안 일어나게 할 수는 없을까요?
⇒ 있다. 변수 선언시 키워드를 var을 쓰지말고 let을 사용하면 된다.
사실 이런 코드를 안 짜면 된다.
방금 전 코드를 구성과 키워드만 바꿔서 보겠다.
console.log(cosmos); // ReferenceError: Cannot access 'cosmos' before initialization
//------ 사실 이 줄 부터는 에러때문에 실행되지 않음.
let cosmos;
console.log(cosmos); // output : undefined
cosmos = "ho-it";
console.log(cosmos); // output : "ho-it"
이번에는 코드의 이상함을 감지하고 에러를 띄워주는 모습이다.
이는 let 키워드의 특성 때문인데, let 키워드로 선언한 변수도 호이스팅은 발생한다. ⇒ 그러나, “선언 단계”와 “초기화 단계”가 분리되어 진행된다. ㅤ ㅤ
위의 말이 복잡하기 때문에 이번에도 순서를 나열해보겠다.
- 코드 실행전, JS 엔진 때문에 이번에도 변수 호이스팅이 발생한다.
- 그러나, 이 때 let의 특성 때문에 선언 단계만 마쳐진다.
- 그대로 첫 줄에 가서 cosmos를 실행시킨다.
- cosmos는 선언만 되어있고, 초기화는 되지 못했기 때문에 에러가 발생하며 멈춘다.
이렇게, let의 경우 선언이 먼저 발생하고 코드 실행 시 실제 변수를 선언이 작성된 부분에 가서야 undefined로 초기화 된다.
⇒ 실행 이전 선언 ~ 실제 초기화 사이의 변수 참조 금지구간을 일시적 사각지대(Temporal Dead Zone) 이라고 한다.
4가지의 긴 순서는 한 마디로 “TDZ단계인 cosmos를 출력하려 했기에 참조에러가 발생했다.” 라고 요약할 수 있는 것이다.
💡 자바스크립트는 ES6에서 도입된 let, const를 포함해서 모든 선언(var, let, const, function, function*, class…)을 호이스팅 한다. 단, ES6에서 도입된 let, const, class를 사용한 선언문은 호이스팅이 발생하지 않는 것처럼 동작한다. ㅤ ㅤ ㅤ ㅤ
함수 호이스팅
바로 다음으로 넘어가서 함수 호이스팅에 대해서도 빠르게 살펴보도록 하겠다.
다음 코드를 확인해보자.
console.dir(add); // f add(x,y)
console.dir(sub); // undefined
console.log(add(2, 5)); //7
console.log(sub(2, 5)); // TypeError : sub is not function
//함수 선언문
function add(x, y) {
return x + y;
}
//함수 표현식
var sub = function (x, y) {
return x - y;
};
결론적으로 말하자면 둘 다 호이스팅이 발생한다.
그러나, 차이가 좀 있다.
이는, 함수 선언문으로 선언한 함수이냐, 함수 표현식으로 선언한 함수이냐의 차이 이긴 한데 설명을 한번 진행해보겠다.
일단, 변수 호이스팅의 경우 undefined로 초기화된다면 함수 선언문에 발생하는 함수 호이스팅의 경우 함수 객체로 초기화한다.
⇒ 그렇기 때문에, 정상 호출이 가능했고 console.dir로 add 호출 시 아무런 참조에러가 없던 것이다.
함수 표현식의 경우 함수의 역활을 하는건 사실이나 변수 sub에 함수 리터럴이 할당되는 구조이기 때문에 함수 호이스팅이 아닌 변수 호이스팅이 발생한다.
⇒ 그렇기 때문에, sub는 마지막 줄 전까지는 값이 undefined인 일반 변수 취급을 받아 에러가 났다.
결론 : 함수를 만들고 싶을 때는 가능하면 함수 선언문을 쓰되, 함수 표현식을 쓰고 싶다면 함수 표현식이 호출되기 윗쪽에 코드를 작성해야 한다.