IIFE(Immediately invoked function expression) 를 사용하는 사례 중 하나는, 비동기적 코드가 정확히 동작할 수 있도록 새 변수를 새 스코프에 만드는 것이다.
var i;
for(i = 5 ; i>= 0; i--){
setTimeout(function(){
console.log(i===0 ? 'go' : i);
},(5-i) * 1000);
}
실제로는 -1이 여섯 번 출력된다. 왜 그럴까? setTimeout에 전달된 함수가 루프 안에서 실행되지 않고, 루프가 종료된 뒤에 실행됐기 때문이다. 따라서 루프의 i는 5에서 시작해 -1로 끝난다. 그리고 -1이 되기 전에는 콜백 함수가 전혀 실행되지 않기에, 콜백 함수가 호출되는 시점에서의 i의 값은 -1이다.
이렇게 해도 결과는 같다.
for(var i = 5 ; i>= 0; i--){
setTimeout(function(){
console.log(i===0 ? 'go' : i);
},(5-i) * 1000);
}
이를 해결해서 5,4,3,2,1,go가 출력되게 하려면 다음과 같이 하면 된다.
for(let i = 5 ; i>= 0; i--){//let 을 사용해 이 블록 수준의 스코프를 만들자
setTimeout(function(){
console.log(i===0 ? 'go' : i);
},(5-i) * 1000);
}
이 경우 자바스크립트는 루프의 단계마다 변수의 복사본을 새로 만든다. 따라서 setTimeout에 전달한 함수가 실행될 때는 독립 스코프에서 변수를 받는다.
도대체 var과 let의 차이점이 뭐길래 이렇게 되는 것일까?🤔
var은 function scoped, let은 block scoped이다.
var은 function안에서 접근할 수 있으며, let은 특정 블록 안에서만 접근할 수 있다. 더 자세한 설명은 여기서!
만약 이 문제를 let을 사용하지 않고 해결하려면 어떻게 해야 할까?
function loopBody(i) {
setTimeout(function(){
console.log(i===0 ? 'go' : i);}
,(5-i)*1000); //(5-i)*1000만큼 기다리고 난 후 console.log 실행
}
var i;
for(i = 5; i>=0;i--){
loopBody(i);
}
함수를 하나 더 쓰면 스코프가 새로 만들어지고, 각 단계에서 i의 값이 클로저에 캡쳐되며 처음에는 5, 두번째는 4..가 전달된다.
물론 더 간편하게 쓰기 위해 IIFE를 쓸 수 있다.
var i;
for(i = 5; i>= 0;i--){
(function(i){
setTimeout(function(){
console.log(i===0 ? 'go' : i);
},(5-i)*1000)
})(i);
}
하지만 이 역시 번거로운 방법이므로, 결론적으로는 아까 살펴보았듯이 block scoped 인 let을 사용해서 해결하는 게 가장 간단하다.