var은 함수레벨 스코프를 let은 블록레벨 스코프를 가집니다.
여기서 블록이란 함수, if문, for문, while문 등 중괄호로 묶은 부분을 의미합니다.
var는 for문에서 스코프를 따로 갖지 않기 때문에 의도치 않게 전역변수의 값이 변경될 수 있습니다.
let은 for문 안에서 무슨 행동을 하건 전역변수에는 영향을 미치지 않습니다.
var i = 1; // 전역변수
for (var i = 0; i < 5; i++) {
console.log(i); // 0, 1, 2, 3, 4
}
console.log(i); // 5 <-- 전역변수 i의 값이 변경되었다 !
let i = 1;
for (let i = 0; i < 5; i++) {
console.log(i); // 0, 1, 2, 3, 4
}
console.log(i); // 1 <-- 전역변수 i의 값이 그대로이다.
var의 특성 때문에 그동안은 for문과 setTimeout 혹은 setInterval을 같이 쓰는 상황에서 클로저 문제가 발생했습니다.
for (var i = 0; i < 10; i++) {
setTimeout(() => {
console.log(i); // ???
}, (i + 1) * 1000);
}
위 예제의 원래 의도는 1초마다 0, 1, 2, ... 9 가 출력되는 것입니다.
하지만 실행해보면 1초마다 10이 10번 반복해서 출력됩니다. // 10, 10, 10, 10, ....
순서를 자세히 알아보기 위해 이곳저곳에 console.log를 넣어보겠습니다.
for (var i = 0; i < 10; i++) {
console.log('for문 바로 밑');
setTimeout(() => {
console.log(i);
console.log('setTimeout 안');
}, (i + 1) * 1000);
console.log('setTimeout 빠져나와서');
}
for문 바로 밑
setTimeout 빠져나와서 // 바로 10번 출력
10
setTimeout 안 // 1초마다 10번 출력
for문이 다 돌고 나서야, 즉 i가 10이 되고 나서야 setTimeout를 실행하는 것을 확인할 수 있습니다.
이는 비동기 관련 문제입니다. for문이 시작되고 나서 setTimeout의 콜백함수도 1초 후에 시작됩니다. 하지만 for문은 엄청나게 빠른 속도로 돌기 떄문에 setTimeout은 이미 다 변해버린 i 를 볼 수밖에 없습니다. 그렇기 때문에 애꿎은 10만 반복해서 출력합니다.
위 코드에서 var만 let으로 변경해 보겠습니다.
for (let i = 0; i < 10; i++) {
setTimeout(() => {
console.log(i); // 0, 1, 2, ... 9 <-- 1초마다 출력
}, (i + 1) * 1000);
}
원하는 대로 동작합니다!
let으로 바꿨다 하더라도 비동기때문에 for문이 실행이 된 후 setTimeout 내부가 실행됩니다.
다른점은 이번에는 let을 이용했기 때문에 for문의 중괄호에 효력이 생겼습니다.
for문이 돌면서 i 가 0인 스코프, 1인 스코프, 2인 스코프 ... 등등을 생성합니다.
이제 setTimeout함수가 실행되면 i 를 for문의 스코프 안에서 찾기 때문에 순서대로 출력이 됩니다.
정보감사합니다~