Front-End

vsoghlv@naver.com

자바스크립트 클로저 활용 시 주의 사항

클로저는 앞서 말했듯이 너무 남발하여 사용하면 안된다. 아래는 클로저를 사용할 때 주의해야 할 점들이다.

클로저는 프로퍼티 값이 쓰기 가능하므로 그 값이 항상 변할 수 있다.

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 값을 넘겨주기 위해 즉시 실행 함수를 사용한다.