클로저는 앞서 말했듯이 너무 남발하여 사용하면 안된다. 아래는 클로저를 사용할 때 주의해야 할 점들이다.
클로저는 프로퍼티 값이 쓰기 가능하므로 그 값이 항상 변할 수 있다.
function outerFunc(argNum) {
let num = argNum;
return function (x) {
num += x;
console.log(num);
};
}
let exam = outerFunc(3);
exam(2); //5
exam(10); //15
위의 예제에서 보이듯이 num 의 값은 자유 변수이므로 계속해서 변한다.
하나의 클로저가 여러 함수 객체의 스코프 체인에 들어간 경우도 있다.
function func() {
let x = 1;
return {
func1: function () {
console.log(++x);
},
func2: function () {
console.log(-x);
},
};
}
let exam = func();
exam.func1(); //2
exam.func2(); //-2
func 에서 반환되는 객체에는 두 개의 함수가 있다. 이때 이 두 함수 모두 자유변수 x 를 참조하며, 각 함수가 호출될 때 마다 x 의 값이 변한다.
루프 안에서 클로저를 활용할 때 주의
function counterSec(num) {
for (var i = 1; i <= num; i++) {
setTimeout(function () {
console.log(i);
}, i * 1000);
}
}
counterSec(3);
//4
//4
//4
1, 2, 3 을 1초 마다 출력을 위해 만든 예제이지만 예상과는 다르게 4 가 연속 3번 출력된다. 이 이유는 간단한데, setTimeout 에서는 자유변수 i 를 참조한다. 하지만 이 함수가 실행될 때는 이미 함수의 실행이 종료되고 i 가 4 인 상태가 된다. 그러므로 4 를 계속해서 출력하는 것이다.
이를 해결하기 위해서는
function counterSec(num) {
for (var i = 0; i <= num; i++) {
(function (currentI) {
setTimeout(function () {
console.log(currentI);
}, currentI * 1000);
})(i);
}
}
counterSec(3);
i 값을 복사해 setTimeout 에 들어갈 함수에 사용해주면 원하는 결과를 얻을 수 있다. 이때 i 값을 넘겨주기 위해 즉시 실행 함수를 사용한다.