function count() {
    var i;
    for (i = 0; i < 10; ++i) {
        setTimeout(function() {
            console.log(i)
        }, 100)
    }
}

count();    // ??

이 코드는 대표적인 클로저 예제입니다. 숫자를 0부터 9까지 출력하고 싶지만, 10이 10번 출력됩니다.

setTimeout()에 인자로 넘긴 익명함수는 모두 0.1초 뒤에 호출될 것입니다. 그 0.1초 동안 이미 반복문은 모두 순회하면서 i는 10이 된 상태이고, 익명함수가 호출되면서 10이 저장된 i를 참조하기 때문입니다.

이를 이해하기 위해서는 자바스크립트의 스코프에 대한 개념이 필요합니다. 렉시컬 스코프에 정리가 매우 잘 되어 있으니 참고하세요!

위 코드를 해결하려면 다음과 같은 2가지 방법을 사용하면 됩니다.

  • 새로운 스코프를 추가하여 반복마다 값을 저장하는 방식
function count() {
    var i;
    for (i = 0; i < 10; ++i) {
        (function(c) {
            setTimeout(function() {
                console.log(c);
            }, 100);
        })(i);
    }
}

count();
  • ES6에 추가된 블록 스코프를 이용하는 방식
function count() {
    'use strict';
    for (let i = 0; i < 10; ++i) {
        setTimeout(function() {
            console.log(i);
        }, 100);
    }
}

count();

참고자료

꿀같은 자료입니당.

JavaScript 클로저(Closure)

자바스크립트의 스코프와 클로저