let 키워드와 var 키워드
let 은 변수를 선언할 때 사용합니다.
- 블록 스코프를 가집니다.
- 호이스팅이 됩니다.
- 단, 초기화 전까지는
Temporal Dead Zone 에 속합니다.
var 도 변수를 선언할 때 사용합니다.
- 함수 스코프를 가집니다.
- 호이스팅이 됩니다.
- 초기화 전에도 참조가 가능하지만,
undefined 라는 값을 가지고 있습니다.
함수 스코프의 특징
function foo() {
for (var a = 0; a < 10; a++) {
console.log(a);
}
console.log(`outside: ${a}`);
}
var 로 for 문을 작성하고 외부에서 a 를 참조해도 참조가 가능합니다.
- 이는 함수 스코프라는 특성 때문입니다. 블록에 갇혀있더라도 함수 내에서는 변수를 계속 참조 가능합니다.
function foo() {
for (let a = 0; a < 10; a++) {
console.log(a);
}
console.log(`outside: ${a}`); // Uncaught ReferenceError: a is not defined
}
let 으로 for 문을 작성하면, 외부에서 a 를 참조할 수 없습니다.
- 이는 블록 스코프라는 특성 때문입니다. 블록이 끝나면, 해당 변수를 참조할 수 없습니다.
var 의 클로저 문제
function bar() {
for (var a = 0; a < 10; a++) {
setTimeout(() => {
console.log(a);
});
}
}
- 위 함수의 결과는
10 만 10 번을 출력하게 됩니다.
- 그 이유는
var 의 함수 스코프 특성과 setTimeout 내부에 있는 콜백 함수가 즉시 처리되지 않고 비동기 작업 큐 에 머무르다 동기 작업이 끝난 후 Event Loop 에 의해 처리된다는 점에 있습니다.
setTimeout() 내부의 콜백 함수는 비동기로 실행되고, 이는 자바스크립트 엔진의 특성상 비동기 작업 큐 에 넘겨져 콜스택에 있는 작업이 완료된 후 Event Loop 에 의해 처리됩니다.
var 로 선언된 변수가 for 바깥에서 유효하다는 특성으로 인해 자바스크립트 엔진은 console.log(a) 코드를 실행할 때, 당시 a 에 들어있던 값을 이용하지 않고, a 의 참조를 이용합니다.
a 는 유효하나, 비동기 작업 큐 에 있는 작업이 Event Loop 에 처리되는 시점에는 for 문이 모두 끝난 시점이라 10 이라는 값을 가지게 됩니다.
function bar() {
for (let a = 0; a < 10; a++) {
setTimeout(() => {
console.log(a);
});
}
}
- 위 코드는
let 의 블록스코프에 의해 아마 모두가 의도한대로 0~9 까지 숫자를 순차적으로 출력하게 됩니다.
var 의 호이스팅과 let 의 TDZ 특성
function foobar() {
console.log(a); // undefined
var a = 100;
}
var 는 자바스크립트 엔진에 의해 해석될 때, 호이스팅이라는 과정을 거칩니다.
- 호이스팅은 코드에서 사용되는 변수나 함수의 이름을 보고, 해당 키워드를 맨 위로 끌어올리는 행위를 말합니다.
- 그래서 위 코드에서
console.log(a) 시점에 a 가 존재하지 않음에도 에러를 내지 않고 undefined 를 출력합니다.
function foobar() {
console.log(a); // Uncaught ReferenceError: Cannot access 'a' before initialization
let a = 100;
}
let 키워드도 동일하게 호이스팅 현상은 일어납니다.
- 단,
let 으로 선언된 변수는 초기화 전까지는 Temporal Dead Zone 이라는 영역에 있어 사용이 불가능합니다.
- 만일 위처럼 사용한 경우에는
Uncaught ReferenceError 가 발생하게 됩니다.
- 이렇게 명확하게 에러를 내주는 것은 프로그래머의 실수를 방지해줍니다.