Execution context에는 세 가지 요소가 있다.
1.variable environemnt
2.scope chain
3.this keyword
이전 포스트에서 scope chain에 대해 정리했고, 이 번에는 변수 환경에서의 호이스팅 개념을 살펴보자.
호이스팅이란?
: 코드가 실행되기 전에 자바스크립트 엔진이 변수 선언과 함수 선언을 메모리에 끌어올려서 사용할 수 있게 하는 것이다.한마디로 코드 실행 전부터 메모리에 변수와 함수를 메모리에 저장해 두는 것이다. 즉, 변수들이 스코프의 가장 맨 위로 끌어 올려지는 것이다.
여기서 let과 const는 호이스팅 안한다고 써있는데 사실 엄밀히 말하면 let과 const도 호이스팅 된다. 하지만 이 둘은 temporal dead zone
이라는것을 가진다. let과 const가 선언되기 전까지를 이 존이라고 하는데 이 TDZ라는 것이 있는 이유는 바로 에러를 피하고 쉽게 코드를 파악하기 위해서이다. 또 다른 이유는 const의 정확한 동작을 보장하기 위해서이다. const는 한 번 선언하면 재할당이 불가능한 특성을 가지기 때문에, 초기화 이전에 접근할 수 없어야 한다.
유데미 Q&A 에서 어떤 분이 설명한 호이스팅이 제일 이해 잘 되서 여기에 적어둔다.
Take a look at this code
test();
function test() {
console.log("Hello");
}
We can call the test() function before it was declared in code. That's the hoisting in practice.
Why it's possible?
JavaScript engine scans the code before executing it and creates a property for each variable or function in the code. For normal variables, it assigns an undefined value, and for functions it assigns a reference to that function in memory. That's why we can call a function, but if we try to access a variable, we will get undefined.
function scope() {
console.log(var1); // undefined
console.log(va1); // undefined
var var1 = "Hello";
var var2 = "Hi";
}
위의 코드를 보면 var로 선언했을 때는 저렇게 선언 전에 console.log로 출력해보면 undefined가 나온다. 즉, 자바스크립트 엔진이 전체적인 코드를 스캔하고 variable을 undefined으로 선언해주는것이다.
예시 코드를 보면서 개념 정리해보자
console.log(me); //undefined
console.log(job);
console.log(year);
var me = 'Jonas';
let job = 'teacher';
const year = 1991;
위 코드에서 var은 호이스팅에 의해 선언은 되지만 값이 undefined로 초기화된다. let과 const는 TDZ에 있기 때문에 초기화 전에 접근할 수 없어 에러가 발생한다
console.log(addDecl(2, 3));
console.log(addExpr(2, 3));
console.log(addArrow(2, 3));
function addDecl(a, b) {
return a + b;
}
const addExpr = function (a, b) {
return a + b;
};
const addArrow = (a, b) => a + b;
마찬가지로 여기서 첫번째 function declaration은 작동이 되지만
function expression과 arrow function은 tdz존에 있기 때문에
콘솔에 이런 오류가 생긴다.
Uncaught ReferenceError: Cannot access 'addExpr' before initialization
function expression과 arrow function을 이렇게 var로 바꾸면
console.log(addExpr(2, 3));
console.log(addArrow(2, 3));
function addDecl(a, b) {
return a + b;
}
var addExpr = function (a, b) {
return a + b;
};
var addArrow = (a, b) => a + b;
Uncaught TypeError: addExpr is not a function
이런식으로 에러가 뜨는데 그 이유는 var로 해서 값이 undefined인 상태에서 2,3을 불러오라고 하니까 이건 function이 아니다라는 에러가 뜨는것이다.
다음으로, hoisting의 안좋은 예를 예시코드와 함께 보도록 하자.
if(!numProducts) deleteShoppingCart();
var numProducts = 10;
function deleteShoppingCart() {
console.log('All products deleted!');
}
위의 코드를 보면 !numProducts 이므로 만약 numProducts가 false값을 가지면 true가 되고 true면 false값을 가지게 된다. 여기서는 numProducts가 10이므로 truthy 값을 가지는데 그럼에도 deleteShoppingCart 함수가 실행되고 All products deleted! 가 출력된다. 그 이유는 var의 호이스팅 때문에 numProducts가 undefined value를 갖게 되고 undefined또한 0처럼 falsey value이기 때문이다.
마지막으로 var, const, let의 또다른 차이점을 보자.
var x = 1;
let y = 2;
const z = 3;
console.log(x=== window.x);//true
console.log(y=== window.y);//false
console.log(z=== window.z);//false
var은 window property도 함께 생성한다. 그래서 윈도우 객체에서 var의 값이 저장되어 있는 것을 볼 수 있다.