클로저 : 전역범위와 함수와의관계가 클로저 관계이다.
비동기와 반복문을 같이쓸 때 클로저 문제가 자주 발생한다.
[문제]
for(var i = 0; i < 100; i += 1){
setTimeout(function(){
console.log(i);
// 결과 i가 100인상태로 1초마다 100이 찍힘.
}, i * 1000)
}
위 코드에서 만들고 싶었던 내용은 0,1,2,3,4,5 ... 99가 찍히는 것이었다.
하지만 100,100,100,100 ... 100 이 찍히는 현상이 일어나고 있고 이 문제를 '클로저문제' 라고들 한다.
for문안에 있는 setTimeout은 '비동기' 이다. 그래서 코드를 위에서 아래로 읽는 javascript의 경우 컴퓨터의 처리속도가 빨라 for문은 이미 100까지 돌아가 있는 상태에서 setTimeout의 콜백안에있는 내용이 실행된다.
그래서 i * 1000의 i 는 0,1,2,3,4 ... 가 되는 반면 setTimeout의 콜백{} 안에있는 i 는 100이 된다.
더 정확히는 비동기의 콜백 안에있는 변수는 선언할때가 아니라 실행할 때 값을 찾는다.
즉, setTimeout 콜백{}안 i는 setTimeout이 실행될 때 값을 0,1,2초뒤에 찾기 때문에 i는 for문의 영향으로 100이 출력된다.
※0초일때 100이 출력되는 이유는 이벤트루프에 있다.
[해결]
for(var i = 0; i < 100; i += 1){ // 0일 때
function 클로저(j){ // i값 -> j = 0;
setTimeout(function(){
console.log(j)
// 렉시컬스코프의 이유로 j = 0;
}, i * 1000); // i = 0;
}클로저(i); // i = 0;
}
for문을 돌릴 때 i = 0이고, 클로저(0)을 실행한다. 클로저(j)는 i의 인자를 받아기때문에 0 이된다.
setTimeout의 콜백{}을 실행할 때 변수 j를 찾는다. 변수j는 스코프체인을 이루어 한단계 밖에 있는 클로저 j에서 0 을 가져온다.
따라서 실행은 1초마다 1,2,3,4, .. 99가 된다.
[참고자료]
var 과 let의 차이
var는 함수 스코프에 해당되고,
let은 중괄호 스코프에 해당된다.
function x(){
{
var t = 1;
}
console.log(t) // 1
}
function x(){
{
let t = 1;
}
console.log(t) // error
}
var는 선언을 중괄호 안에 하고 실행을 중괄호밖에 해도 실행이 된다. 그 이유는 함수안에있기 때문이다.
반대로 let은 선언을 중괄호안에하고 실행을 밖에서 하면 실행이 되지 않는다. 그 이유는 let은 현재 중괄호안에 묶여있기 때문이다. 그래서 console.log(t)를 중괄호 안에 넣어주면 실행이 된다.
[참고자료]