for문 안에 settimeout 함수 설명>
질문> 1 2 3 이 출력되길 원하는데 왜 3 3 3 이 찍힐까?
답>
클로져 문제이다.
일단
test = function () {
for (i=0; i < 3; i++) {
setTimeout(function() {
console.log(i);
}, 3000*i);
}
}
이 코드에서 test();를 하면 for문에서 3번 반복을 한다.
그럼
setTimeout(function() {
console.log(i);
}, 3000*i);
이 부분이 세번 실행이 된다
그럼 실행이 된다는 소리는
곧 3 개의 함수가 만들어진다는 의미이다.
그래서
3개의 함수는
js에서 이벤트 루프가 처리하는 이벤트 큐로 들어가게된다.
그럼 ... 근데
이벤트 루프에서 스택에서 정해진 작업이 끝나면
큐에서 하나씩 뺴서 실행을 그제서야 한다
먼저
첫번쨰
setTimeout(function() {
console.log(i);
}, 3000*i);
이걸 실행한다
실제로는
function() {
console.log(i);
이걸 실행하는거다
이 말은 즉슨, i는 값이 없는거다 아직은!!!
setTimeout(function() {
console.log(i);
}, 3000i); 까진 이미 이벤트 루프에 들어가서 3000i는 3000*1로 해서 들어가요 큐로
test = function () {
for (i=0; i < 3; i++) {
setTimeout(function() {
console.log(i);
}, 3000*i);
}
}
이 함수를 정의했을때
i 값을 아직 몰른다
test(); 를 실행하면
for (i=0; i < 3; i++) {
이게 반복하면서
}
setTimeout(function() {
console.log(i);
}, 3000*1);
setTimeout(function() {
console.log(i);
}, 3000*2);
setTimeout(function() {
console.log(i);
}, 3000*3);
이렇게 3번을 반복해서 큐로 집어 넣는다
그럼
3초 6초 9초 (가 되는 것이다) 일때
이벤트 루프는
(function() {
console.log(i);
이부분을 실행한다
i의 바인딩 시점이 다른것이다.(i 의 바인딩 시점은 큐에서 스택으로 꺼내올때 그제서야 i의 값을 바인딩해서 3이 되는 것이다.)
여튼
이벤트 루프는
function() {
console.log(i);
이 부분을 실행하려고 하고
그럼 i 를 찾겠죠
이때 찾는 영역이 함수 스코프 이죠
함수 스코프는 함수안의 영역을 말하고
즉, i 값이 이 함수내에 없다는걸 알고는
(저때 var i = 라는건 존재하지않는다)
그 다음 스코프 (상의 영역스코프) 로 가서 찾는다
test = function () {
for (i=0; i < 3; i++) {
}
}
여기 말하는 거다.
근데 여기보니까 for문에서 쓰는 i 가 있다 (i 발견!!!!)
그래서 i 를 찾았는데 , i = 3이다
왜냐면
이벤트 루프에서 셋타임함수가 실행될때 (총 3번)
바로 실행 안되고 큐로 넘어간다 했잖아
그 넘어갈때 셋타임 함수는 큐로 넘어가는데,
for문은 넘어가지않는다 그니까
즉 for문 안에 있는 애가 3번이 실행되는데 그 실행되는애가 셋타임함수고
셋타임함수는 큐로 넘어가서 마지막에 실행이되고
for문 자체는 그냥 아무값도 없이 3번만 반복하게되서 i는 3값만 카운팅이 마친 상태이다.
그래서
스택이(for문이 다돌고) 큐(셋타임함수)를 이제 가져와서 스택에서 실행이 될때
이미 스택에서 실행을 마친 for문의 i 값이 3이니까(3번 카운팅이 마친상태)니까
셋타임함수안의 함수가 실행이 되면(가장늦게) i는 3 이걸 가져오는 것이다.
다시 정리하자면!!
test();로 함수가 호출이 되면
for (i=0; i < 3; i++) {
}
이 세번 반복하면서
setTimeout(function() {
console.log(i);
}, 3000*1);
setTimeout(function() {
console.log(i);
}, 3000*2);
setTimeout(function() {
console.log(i);
}, 3000*3);
이렇게 3번 반복이되는데 ,이 값을 이벤트루프 큐에 넣어버린다(비동기니까)
(이때 실행되는건 오로지 for문의 자체 카운팅만 3번 실행이되고 for문 안의 셋타임함수는 지금 실행이 되지 않고 큐로 넣는다는 소리이다.)
그리고 각각 3 6 9초 후에 이벤트 루프는 저것을 처리하는데,
첫번째
function() {
console.log(i); 을 처리하려고 보니까
i 를 찾아야하고
function() {
console.log(i)
} 이 함수 스코프에는 i의 값이 없어요
그래서 자신의 부모 함수 영역에서 i를 찾으려고하죠
그래서
function () {
for (i=0; i < 3; i++) {
console.(i)
}
여기가 부모 함수 영역이고
i가 있길래 가져오려고 보니까
i는 이미 3으로 증가되었어요
그래서 3을 가져와요
왜냐면
function () {
for (i=0; i < 3; i++) {
}
저 함수는 이미 맨처음에 이벤트루프 큐에 넣을때 실행을 하죠
test(); 실행할때 말이에요
그래서 반복문만 3번 돌면서
i++
하면서요
그러면서
마지막에 큐에서 가져와서 실행할떄는
이미 부모영역
function () {
for (i=0; i < 3; i++) {
}
}
여기는 이미 반복문 다돌고
자신의 역할을 모두 끝낸상태예요
흔적만 남아있는거죠
그리고
그리고 보시면 저당시의
}, 3000*i);
의 i는
각각 1 2 3 으로 바인딩돼요
문제는
function() {
console.log(i);
이건데 여기선 이건 실행안하죠
왜냐하면
함수 호출을
setTimeout(함수 리터럴(문자 그자체)
}, 시간);
이렇게 해서
이벤트 류프 큐에 담고
저 함 수 리터럴이
의미를 가지려면
호출을 해야하는데
그 시점이 3초 6초 9초로 비동기로 되있거든요 그럼
스택에서 모두 다 처리한다음 큐에서 3초후실행 하고 그제서야 꺼내와서
function() {
console.log(i);}
이걸 실행시키죠
그래서 i 값을 결국 부모에서 찾고(클로져)
여기서 i값이 클로져입니다
i 가 자기 함수영역에 없으면 부모영역에서 찾는걸 클로져라고합니다 그래서 i는 클로져인거죠
여기서
또 알아야할 점은
i는 다른 코드에선 접근 불가하고
일단 자신의 스코프 영역인
익명함수 function() {
console.log(i); 만 접근가능하다는 사실이죠(일단 자식 스코프영역-> 부모영역)
여튼
이 코드의 해결책은
test = function () {
for (i=0; i < 3; i++) {
(function(x) {
setTimeout(function() {
console.log(x);
}, 1000*x);
})(i);
}
}
이건데
익명 즉시 실행함수만 붙은거다.
(function(x) {
콘솔로그
})(i);
이렇게
그래서
test를 실행하면
(function(1) {
setTimeout(function() {
console.log(x);
}, 1000*1);
})(1);
처음에
이런식으로 실행된다
setTimeout(function() {
console.log(x);
이 부분은 나중에 실행이 되요
아까처럼
그럼 나중에 x값을 찾겠죠
아까는 test 함수 스코프 - > console.log(i) 이런식으로
체인이였지만
저건
test 함수 스코프 - >익명함수 스코프 - > console.log(i) 함수 스코프
중간에 익명함수 스코프가 끼어들죠
익명즉시 실행함수 니까
저건 즉시 실행이 되니까
i 가 증가할때마다
x값을 1, 2, 3 즉시 실행이 되고
값을 가지고 있는거죠
즉시 함수가 실행되면서
제일 쉬운 방법은 let을 사용하는거죠 블락 스코프~!