javascript & Node.js/javascript

자바스크립트 클로저

반응형

1. 클로저란?

어떤 함수 A에서 선언한 변수 a를 참조하는 내부 함수 B를 외부로 전달할 경우 A의 실행 컨텍스트가 종료된 이후에도 변수 a가 사라지지 않는 현상 (외부 함수의 LexicalEnvironment가 가비지 컬렉팅되지 않는 현상.)

/**
 * outer()에서 변수 a를 선언했고, outer의 내부함수인 
 * inner 함수에서 a의 값을 1을 증가.
 */
const outer = function () {
  let a = 1;
  const inner = function () {
    // inner 함수 내부에서 a를 선언하지 않아서 environmentRecord에서
      // 값을 찾지 못해 outerEnviornmentReference 에서 
    // 지정된 상위 컨텍스트인 outer의 LexcialEnviornment에 접근해서 a를 찾음.
    console.log(++a);
  };
  inner();
};

outer();
  • outer의 실행 컨텍스트가 종료되면, LexicalEnvironment에 저장된 식별자들(a, inner)에 대한 참조를 지움.
  • 각 주소에 저장돼 있던 값들은 자신을 참조하는 변수가 하나도 없게 되므로 가비지 컬렉터의 수집 대상이 된다.


클로저

const outer = function () {
  let a = 1;
  const inner = function () {
    return ++a;
  };
  return inner; // inner 함수의 실행 결과가 아닌 inner 함수 자체를 반환한다.
};

const outer2 = outer(); // outer 함수의 실행 컨텍스트가 종료될 때 outer2 변수는 outer의 실행 결과인 inner함수를 참조하게 된다.
console.log(outer2());
console.log(outer2());
  • innner 함수의 실행 시점에는 outer 함수는 이미 실행이 종료된 상태인데 어떻게 outer 함수의 LexicalEnvironment에 접근할 수 있을까? => 가바지 컬렉터의 동작 방식 때문에 가능함.
  • 가바지 컬렉터는 어떤 값을 참조하는 변수가 하나 있다면 그 값은 수집 대상에 포함시키지 않는다.
  • outer()는 실행 함수는 inner 함수를 반환해서 외부 함수인 outer()의 실행이 종료되어도 내부 함수인 inner 함수는 언제든지 outer2를 실행함으로써 호출된 가능성이 열린것.
  • 따라서 가바지 컬렉터의 수집 대상에서 제외가 되서 inner 함수가 이 변수에 접근할 수 있게 된다.


2. 클로저와 메모리 관리

메모리 누수의 위험을 이유로 클로저 사용을 조심해야 한다거나 심지어 지양해야 한다고 주장하는 사람들도 있지만 메모리 소모는 클로저의 본질적인 특성일 뿐이다.

오히려 이러한 특성을 정확히 이해하고 잘 활용하도록 노력해야 한다.

클로저는 어떤 필요에 의해 의도적으로 함수의 지역변수를 메모리를 소모하도록 함으로써 발생한다. 그러면 그 필요성이 사라진 시점에는 더는 메모리를 소모하지 않게 해주면 된다.

참조 카운트를 0으로 만들면 언젠가 GC가 수거해갈 것이고, 이때 소모됐던 메모리가 회수된다. (식별자에 참조형이 아닌 기본형 데이터(null or undefined 등)을 할당하면 된다.

반응형