호이스팅이란 스코프(scope)안에서의 변수 선언을 최상위에서 선언한 것과 동등하다는 의미를 가진다.
변수가 선언되기 이전에 변수를 사용하면, 변수가 사용된 블럭 범위의 맨 위로 변수를 끌어올린다는 말이다.
var myVar='외부 scope';
function test(){
console.log(myVar); // undefined
var myVar='내부 scope';
};
test();
위와 같이 선언부를 최상단으로 끌어올렸기 때문에 선언만 되고 값은 할당되지 않은 상태에서 호출을 하게 되어 undefined를 출력한 것이다.
그 이유는 var키워드로 선언된 변수는 선언단계와 초기화단계가 한번에 이루어지는데, scope(스코프)안에서 선언된 변수는 항상 최상위에 선언한 것과 동등한 의미를 가지기 때문이다.
즉, 호이스팅(hoisting)이 발생했기 때문이다.
위 예제에서 작성했던 코드에서 호이스팅이 발생하면 다음과 같이 인식한다.
var myVar='외부 scope';
function test(){
var myVar; //호이스팅 발생 , 선언과 동시에 undefined로 초기화가 진행
console.log(myVar); //그래서 undefined가 출력
var myVar='내부 scope';
};
test();
이처럼 호이스팅이 발생하면 test함수에서 스코프안에서 변수 myVar가 최상위에서 선언되는 것으로 인식하게 된다.
호이스팅은 JavaScript 인터프리터가 코드를 해석할 때 변수 및 함수의 선언처리, 실제 코드 실행의 두 단계로 나눠서 처리하기 때문에 발생하는 현상이다.
하지만 Javascript 인터프리터가 내부적으로 코드를 이런 방식으로 처리한다는 것일 뿐, 실제 코드라인이 변경되거나 하는 건 절대 아니다.
또 var로 선언된 변수는 호이스팅으로 인해 변수가 선언되기 전에도 변수를 사용할 수도 있다.
a='test';
var a;
//호이스팅으로 인해 아래와 같은 결과가 나온다.
var a;
a='test' //test2
사람들은 보통 let과 const는 호이스팅(Hoisting)을 수행하지 않는다라고 생각한다.
하지만 그것은 잘못된 생각이며 var와 똑같이 호이스팅이 발생한다.
많은 사람들이 let과 const선언에서 호이스팅(Hoisting)이 발생하지 않는다라고 느껴졌던 이유는 바로 let과 const에서 TDZ에 의해 제약을 받는다는 것을 모르고 그냥 오류가 발생했다고 생각했기 때문이다.
TDZ란 선언은 되어있지만 아직 초기화가 되지 않아 변수에 담길 값을 위한 공간이 메모리에 할당되지 않은 상태를 말한다.
그림 출처 : https://dmitripavlutin.com/javascript-variables-and-temporal-dead-zone/
그림은 본 다음 아래 예제를 보면, 아래 예제에서는 변수를 초기화되기 전에 액세스를 하려고 한다.
console.log(name); //throws a ReferenceError
const name='Kim';
하지만 결과는 var처럼 undefined를 반환하지 않고, ReferenceError가 발생한다.
이것은 바로 TDZ때문이며, 이는 코드 예측을 가능하게 하고 버그를 쉽게 찾아낼 수 있게 해주는 장점이 있다.
이러한 장점때문에 var를 쓰지 않고, const와 let을 써야하는 이유가 명확해진다.
// const name에 값을 할당하기전에 name에 접근하면, TDZ에 의해 ReferenceError가 발생하게 된다.
console.log(name); // throws a ReferenceError
const name = 'Kim';
// 반대로 name이 실행된 이후에 접근 가능
const name = 'Kim';
console.log(name); // Kim
위의 예제와 같이 실행되지 전까지 액세스 할 수 없는 현상을 TDZ라고 부른다.
아래 예제와 같이 let은 변수에 재할당이 가능하지만 재선언이 불가하며,
const는 변수에 재선언과 재할당이 모두 불가능하다.
이것 또한 TDZ에서 제약을 받고 있기 때문에 오류가 발생하는 것이다.
//var변수 a를 재선언하였지만 아무런 문제가 발생하지 않는다
var a = 'test'
var a = 'test2'
console.log(a) //test2
b = 'test'
var b
console.log(b) //test
//let은 변수에 재할당이 가능하지만 재선언은 불가하다.
let c = 'test'
let c = 'test2' //변수에 재선언 불가
console.log(c) //SyntaxError: Identifier 'c' has already been declared
let d = 'test'
d = 'test3' //변수에 재할당 가능
console.log(d) //test3
//const는 변수 재선언, 재할당 모두 불가능하다.
const e = 'test'
const e= 'test2' //SyntaxError: Identifier 'e' has already been declared
e = 'test3' //"SyntaxError: Invalid or unexpected token
console.log(e)
var에서만 호이스팅이 발생하는 것은 아니다!
즉, 모든 변수 선언에서는 호이스팅이 발생한다.
let과 const에서의 호이스팅은 let과 const는 스코프에 진입할 때 변수만 만들어지면 TDZ가 생성되는 것이기 때문에 변수가 액세스 할 수가 없는 것이다.
즉, let과 const변수가 완전히 선언(초기화 또는 값 할당)되면, TDZ를 떠나게 될것이고 떠난 상태가 되면 그때부터 변수를 사용할 수 잇게 된다.
호이스팅은 스코프를 꼬이게 만들고 코드의 가독성과 유지보수에 해로우므로 웬만하면 var보단 let,const를 사용하는 것이 좋다.
참고사이트 :
https://tibetsandfox.tistory.com/23
https://ljtaek2.tistory.com/136?category=863722