1. 클로저의 의미 및 원리

 -A closure is the combination of a function and the lexical environment within that function was declared. -MDN-

 -클로저는 함수와 그 함수가 선언될 당시의 lexical environment의 상호관계에 따른 현상

 -여기서 함수가 선언될 당시의 lexical environment는 lexical environment의 outerEnvironmentReference에 해당

 

[코어 자바스크립트] 02. 실행 컨텍스트

1. 실행 컨텍스트 1) 실행 컨텍스트: 실행할 코드에 제공할 환경 정보들을 모아놓은 객체 -선언된 변수, 함수 등을 실행 컨텍스트에 저장해두었다가 호출하면 나옴 2) 스택(stack): 우물 같은 구조로

data-science-study.tistory.com

 -윗글을 참조하면  outerEnvironmentReference는 현재 컨텍스트 바깥의 컨텍스트에 접근할 수 있도록 바깥의 컨텍스트를 저장

     ex) 컨텍스트 A → 내부에 존재하는 내부함수 B

          ▶내부함수 B의 실행 컨텍스트가 활성화된 시점에서 B의 outerEnvironmentReference가 A의 lexical environment를 참조하므로 A의 변수에 접근 가능

          ▶A는 B에서 선언한 변수에 접근 불가능

 -예시에서 B가 A의 변수에 접근가능하여 사용하면 B와 A가 상호작용을 한 것이지만 A의 변수를 무조건 쓰지는 않으므로 상호작용 하지 않을 수도 있음

 -즉, 내부함수에서 외부 변수를 참조하는 경우에 한해서만 combination이 발생

 

 -결론적으로, 어떤 함수에서 선언한 변수를 참조하는 내부함수에서만 발생하는 현상

     (컨텍스트 A에서 선언한 변수를 내부함수 B에서 참조하는 경우에 클로저 발생)

 

 -예시 1)

var outer = function() {
  var a = 1;
  var inner = function() {
    console.log(++a);
  };
  inner();
};
outer();

  →outer 함수에서 변수 a를 선언

  →outer 함수 내에서 inner 함수 선언

  →inner 함수 내에서 a의 값을 1 증가시킨 후 출력

  →inner 함수 내에 변수 a가 선언되지 않았으므로 environmentRecord에서 값을 찾지 못하고 outerenvironmentReference에 지정된 outer 함수의 environmentRecord에 접근하여 outer 함수에서 지정된 변수 a에 접근

  →따라서, '2' 출력

 

 -예시 2)

var outer = function() {
  var a = 1;
  var inner = function() {
    return ++a;
  };
  return inner();
};
var outer2 = outer();
console.log(outer2);

  →이번에는 outer 함수 내에서 inner() 함수의 실행 결과(2)를 반환

  →outer 함수의 실행 컨텍스트가 종료된 시점 이후 a 변수와 inner 함수는 사라질 것

  →outer2에는 inner 함수의 실행결과인 '2'가 저장되어, '2'가 출력되며 outer2를 이후에 반복해서 출력해도 '2'만 출력

 

 -예시 3)

var outer = function() {
  var a = 1;
  var inner = function() {
    return ++a;
  };
  return inner;
};
var outer2 = outer();
console.log(outer2());
console.log(outer2());

  →inner 함수 자체를 반환하여 outer2에는 inner(a에 1씩 더한 값을 반환하는) 함수가 저장됨

  →예시 2)에서 outer2 = 2였다면 예시 3)에서 outer2 = function() { return ++a }

  →따라서, outer2()를 실행할 때마다 ++a, 즉 a에 1씩 더해져 반환되고 반복해서 실행하면 계속 1씩 더해진 값을 가질 수 있음

  →outer 함수의 실행 종료 시점에서 inner 함수를 반환하고 inner 함수를 반환하고 나면 outer 함수는 종료된 상태

  →이 때, inner 함수는 outer2에 저장되어 outer2를 실행하면 언제든 호출될 가능성이 열린 것이므로 inner 함수가 참조해야하는 outer 함수의 LexicalEnvironment는 가비지 콜렉터의 수집대상에서 제외됨

 

 -예제 3)을 통해 알 수 있는 클로저의 정의

  →어떤 함수 A(outer)에서 선언한 변수(a)를 참조하는 내부함수 B(inner)를 외부로 전달(outer2에 inner 함수 자체를 반환)할 경우, A의 실행 컨텍스트 종료 이후에도 변수가 사라지지 않는 현상

  →이때, 외부로 전달이 return만 의미하는 것은 아님

 

 -예시 4)

( function () {
    var a = 0;
    var intervalId = null;
    var inner = function () {
      if (++a >=10 ) {
        clearInterval(intervalId);
      }
      console.log(a);
    };
    intervalId = setInterval(inner, 1000);
})();

  →setInterval에 전달할 콜백 함수 inner는 inner 외부에서 선언된 변수 a를 참조

 

 -예시 5)

( function () {
    var count = 0;
    var button = document.createElement('button');
    button.innerText = 'click';
    button.addEventListener('click', function () {
      console.log(++count, 'times clicked');
    });
    document.body.appendChild(button);
})();

  →'click'이라고 쓰인 버튼을 생성하고, 버튼을 클릭할 때마다 count가 1씩 더해진 값과 'times clicked' 문자가 함께 출력

  →이때, addEventListener에서 실행할 함수 내부에서 외부에 있는 변수 count를 참조함

 

 -예제 5), 6) 모두 지역변수(a, count)를 참조하는 내부함수(inner, function)를 외부(setInterval, button)으로 전달했기 때문에 클로저

 

 

2. 클로저와 메모리 관리

 -클로저는 어떤 필요에 의해 의도적으로 함수의 지역변수가 메모리를 소모하도록(a가 사라지지 않고 계속 참조되도록 하는 등) 함으로써 발생

 -필요성이 사라진 시점에서 메모리를 더 소모하지 않도록 해주면 됨

 -참조 카운트를 0으로 만들어 더 이상 참조되지도 않고 쓰이지도 않게 된다면 메모리가 회수될 것

 -이 때, 참조 카운트를 0으로 만드는 방법으로 null이나 undefined를 할당하면 됨

 

 -예시 1)

var outer = (function () {
  var a = 1;
  var inner = function () {
    return ++a;
  };
  return inner;
})();
console.log(outer());
console.log(outer());
outer = null; // outer 식별자의 inner 함수 참조를 끊음

  →outer 함수를 null로 정의하며 outer 함수 내부에서 변수 a를 참조하던 inner 함수를 없애며 a를 참조하는 컨텍스트가 아무것도 없도록 만듦

 -예시 2)

( function () {
    var a = 0;
    var intervalId = null;
    var inner = function () {
      if (++a >=10) {
        clearInterval(intervalId);
        inner = null;  // inner 식별자의 함수 참조를 끊음
      }
      console.log(a);
    };
    intervalId = setInterval(inner, 1000);
})();

  →함수 내부에서 변수를 참조하던 내부함수 inner 자체를 null로 정의하면 함수 내부변수 a룰 참조하는 것이 없도록 만듦

 

 -예시 3)

( function () {
    var count = 0;
    var button = dovument.createElement('button');
    button.innerText = 'click';
    
    var clickHandler = function () {
      concole.log(++count, 'times clicked');
      if (count >= 10) {
        button.removeEventListener('click', clickHandler);
        clickHandler = null;  // clickHandler 식별자의 함수 참주 끊음
      }
    };
    button.addEventListener('click', clickHandler);
    document.body.appendChild(button);
})();

  →count 변수를 계속 참조하며 button에 eventListener로 주는 함수 clickHandler를 null로 정의하며 count를 참조할 컨텍스트를 없앰

+ Recent posts