✨ 결과물 : 1초마다 0,1,2,3이 출력되게 만들자 🛠
for(var i =0; i <4; i++){ setTimeout(()=>{ console.log(i) },1000) } // 4(4)
var가 전역변수로서 위치하고 있다. setTimeout 함수는 비동기 함수로써 1초 뒤 태스크 큐에 담기게 된다. for문을 모두 돌고나서 태스크큐의 작업 내용이 콜스택으로 이동하여 콜백함수(()=>console()
)를 실행하게 되는데 이 때 필요한 i는 각 실행함수의 컨텍스트에 존재하지 않으며,
i는 스코프 체인을 타고 전역변수 i를 참조하여 약 1초뒤 4를 4번 반환하게 되는 것이다.
for(var i=0; i<4; i++){
((i)=>setTimeout(()=>console.log(i), 1000*i))(i)
}
//0
//1
//2
//3
for문 내부의 함수가 i 값을 인자로 받아 즉시실행함수로 바로 실행되고 setTimeout함수를 반환하는 클로저의 모습을 보여준다. 해당 함수는 태스크큐로 이동하고 for문을 모두 돌아 콜스택이 비워지는 시점에 큐에 저장했던 setTimeout함수를 실행하게 된다.
1초가 지날때 마다 setTimeout함수 내부의 콜백함수를 다시 큐에 담아두게 되고, 다시 한번 콜 스택이 비워지면 큐에서 console.log
함수를 실행, 출력하게 된다. 이 때 i값은 클로저의 특성으로 스코프체인을 타고 setTimeout함수에서 i를 찾아본다. 해당 컨텍스트는 i값이 없기 때문에 한 단계 더 상위함수였던 즉시실행함수에서 i값을 참조하게 되어 i를 출력할 수 있게 된다.
for(let i=0; i<4; i++){
setTimeout(()=>{
console.log(i)
},1000*i)
}
let은 var와 다르게 블록스코프를 갖는다.
(for문 자체를 하나의 블록으로 볼 수 있다.)
때문에 for문 내부의 함수를 실행시킬 때마다 스코프를 생성하게 된다.
1초가 지날 때마다 console.log
콜백함수를 태스크 큐로 이동시키고, 스택이 비워질 때 큐의 콜백함수를 실행하는 시점에서의 i는 각 함수마다 생성되었던 scope의 i를 참조하게 된다.
let i = 0
let counter = setInterval(()=>{
console.log(i++)
if(i>=4) clearInterval(counter)
},1000)
const funcs = []
for(let i =0; i <4; i++){
funcs.push(()=>console.log(i))
}
funcs.forEach((func,idx)=>setTimeout(func, 1000*idx))
여기서 let
대신 var
를 사용하게 되면 i는 4를 가르키게 된다.