예전에 JS의 클로저에 대해 정리하던 중 응용예시에 대해 잘못된 부분이 있다는 지적을 받았었다.
그때도 확인후 '아 수정해야지' 해놓고 다른 걸 하다보니 미루고 미루다 지금이 되었다..;;
근데 문득 생각이 나서 열어보고 이제서야 수정했고, 다시 오류였던 부분에 대해서 직접 실행해보며 체크해보았다.

이게 이전 포스팅에 써있던 글이다. 저 글에 써있는 실행문대로면 결과가 0~4가 안나오긴한다.
결과는 5, 5, 5, 5, 5가 나오는데 지적에서 오류라고 한 이유는 원하는 결과가 나오지 않는 이유가 틀렸기 때문.
var arr = []
// 첫번째 for문
for(var i = 0; i < 5; i++){
arr[i] = function(){
return i;
}
}
// 두번째 for문
for(var index in arr) {
console.log(arr[index]());
// 결과: 5, 5, 5, 5, 5
}
예시의 실행문을 실행하면 결과는 위와 같은데, 이전 글에서는 원하는 결과가 안 나오는 이유로 클로저의 입장에서 접근가능한 외부함수의 지역변수냐를 이유로 들었으나 이 이유는 틀렸다.
저기에서 결과가 제대로 나오지 않는 이유는 변수선언의 스코프 차이 때문이었다.
var는 함수단위 스코프를 지닌다. 그렇기 때문에 바꿔말하면 함수 내에서 선언된 var가 아니라면 어디서든 접근이 가능하다.
스코프에 대한 자세한 설명은 이전 글 참고
즉, 위에선 for반복문에서 var를 사용했고, 이는 window객체의 전역 렉시컬 환경으로 호이스팅된다.
그리고 이후 var i는 불려올 때마다 하나의 i를 참조한다.
또한 하나 더 알아야할 점이 있다. 처음 반복문 실행 후 arr를 콘솔에 찍어보면 안에는 number가 아니라 실행 전인, 모두 같은 형태의 함수가 담겨있다는 것.

그렇기 때문에 아직 arr[i]내부의 function이 실행되지 않은 시점에서 i는 return되지 않았고 나중에 두번째 for문에서 arr[index]()로 실행될 때가 되어서야 "i 참고해야지~" 하고 i를 불러온다는 것.

요약
var가 함수에서 선언된 것이 아니므로 전역 렉시컬로 호이스팅
- 어디서든 접근가능하며
var i는 불려올때 모두 하나의 i를 참조- 첫번째 for문 실행까지 arr안에는 종료되지 않은 함수들이 담겨있다.
- 두번째 반복문에서 arr안의 함수들이 불려올때가 되어서야
return i로 인해i를 참조한다.
- 즉, 모든
arr[index]()의 실행에서return i가 같은 하나의 i를 참조하므로 모두 같은 5라는 값이 나온것.
이를 수정하기 위한 쉬운 방법으로는 var선언이 아닌 let선언을 이용하는 것.
let은 블록 스코프를 가지므로 반복문의 실행때마다 각자 변수마다 다른 스코프(새로운 렉시컬환경)를 형성한다.
var arr = []
// 첫번째 for문
for(let i = 0; i < 5; i++){
arr[i] = function(){
return i;
}
}
// 두번째 for문
for(var index in arr) {
console.log(arr[index]());
// 결과: 0, 1, 2, 3, 4
}
이렇게 하면 각각 실행마다 다른 let을 참조하므로 문제없이 원하는 값을 얻을 수 있다.

근데 문득 잘못된 실행에서 for문의 실행을 i<5까지로 제한해서 4까지만 실행되는데 왜 결과는 4, 4, 4, 4, 4가 아니라 5, 5, 5, 5, 5가 나오는지 의문이었다.
원인이 뭘까 고민해보면서 뇌피셜을 굴려보면 for (var i=0; i<5, i++)로 for문의 범위가 정해져있는데 반복문의 실행은 i<5로 인해 0~4까지로 제한되지만 i++은 i=4인 상태에서 한번더 실행되서 var i = 5는 결과로 마무리되는게 아닐까? 하는 생각.
이게 맞은가 싶으면 찍어봐야지? 그래서 바로 찍어봤다.
일단 반복문 안에서 i가 어디까지 돌아가는지 한번 더 체크하고, 반복문의 종료 후에는 어떻게 찍히는지 봤다.

오메야 맞는갑소.. 이 결과는 let으로 돌려도 똑같으니 var와 let의 차이가 아니라 반복문 자체의 원인인 것 같다.

참고로 블록단위 스코프를 지니는 let과 const는
{}단위로 스코프가 나뉘기 때문에()안에서 선언된 것은 for문 안이라고 보지 않기 때문에 for문밖에서도 접근이 가능한 것(이라고 나는 이해하고 있다)
이를 기반으로 해석해보자면
{}안이라는 것i=4일때 반복문이 돌아가면서 i++까지 한번더 마치기 때문에 마무리된 i=5라는 값을 가지게 되는것!여기까지가 내가 내린 해석과 결론이다.