[코어 자바스크립트] 5.클로저

홍예찬·2021년 1월 14일
0
post-thumbnail

5-1. 클로저의 의미 및 원리 이해

클로저의 개념은 여러 함수형 프로그래밍 언어에서 등장하는 보편적인 특성입니다.
(추후 객체지향 프로그래밍, 함수형 프로그래밍에 대해 블로깅할 예정입니다.)

클로저에 대해 여러 서적에서는 다양하게 정의하지만, MDN에서는 클로저에 대해
함수와 그 함수가 선언될 당시의 Lexical Environment의 상호관계에 따른 현상이라 설명하고 있습니다. 여기서 Lexical Environment는 outerEnvironmentReference에 해당합니다.
(스코프, 스코프 체이닝의 개념이죠!)

더 쉽게 설명하자면 내부함수에서 외부 변수를 참조하는 경우combination 즉, 선언될 당시의 LexicalEnvironment와의 상호관계를 의미합니다.


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

// 2
var outer = function () {
  var a = 1;
  var inner = function () {
    return ++a;
  }
  return inner();
}
var outer2 = outer();
console.log(outer2);  // 2
  1. inner 함수 내부에서는 a를 따로 선언하지 않았기 때문에 environmentRecord에서 값을 찾지 못하므로 outerEnvironmentReference에 지정된 상위 컨텍스트인 outer의 LexicalEnvironment에 접근해서 a를 찾게 됩니다.
  2. outer 함수의 실행 컨텍스트가 종료되면 LexicalEnvironment에 저장된 식별자들(a, inner)에 대한 참조를 지웁니다.
  3. 그러면 각 주소에 저장되어 있던 값들은 자신을 참조하는 변수가 하나도 없게 되므로 가비지 컬렉터의 수집 대상이 됩니다.

위의 두 코드는 outer함수의 실행 컨텍스트가 종료되기 전에, inner 함수의 실행 컨텍스트가 종료돼있으며, 이후 별도로 inner함수를 호출할 수 없습니다. 그렇다면 outer의 실행 컨텍스트가 종료된 이후에도 inner함수를 호출할 수 있게 만들 수 있을까요?



var outer = function () {
  var a = 1;
  var inner = function () {
    return ++a;
  }
  return inner;
}
var outer2 = outer();
console.log(outer2());		// 2
console.log(outer2());		// 3
  1. 위의 두 코드와의 차이점은 inner함수의 실행 결과가 아닌 inner함수 자체를 반환한 것입니다.
    그렇게 되면 outer 함수의 실행 컨텍스트가 종료될 때 (var outer2 = outer();)
    outer2 변수는 outer의 실행 결과인 inner함수를 참조하게 됩니다.
    이후 9번째에서 outer2를 호출하면 앞서 반환된 함수인 inner가 실행됩니다.
  2. inner 함수의 실행 컨텍스트인 environmentRecord에는 수집할 정보가 없습니다. outerEnvironmentRecord에는 inner 함수가 선언된 위치의 LexicalEnvironment가 참조복사 됩니다. (outer 함수의 LexicalEnvironment)
  3. 위의 두 함수와의 차이점이 여기서 발생하게 됩니다. inner 함수의 실행 시점에는 outer 함수가 이미 실행 종료됐음에도 불구하고 변수 a에 접근할 수 있다는 것이죠. 그 이유는 가비지 컬렉터(GC)의 동작 방식 때문입니다. 가비지 컬렉터는 어떤 값을 참조하는 변수가 하나라도 있다면 그 값은 수집 대상에 포함하지 않습니다.
    외부 함수인 outer가 실행이 종료되더라도 내부 함수인 inner 함수의 실행 컨텍스트가 활성화되면 outerEnvironmentReference가 outer 함수의 LexicalEnvironment를 필요로 하기 때문에 수집 대상에서 제외됩니다.

이처럼 함수의 실행 컨텍스트가 종료된 후에도 LexicalEnvironment가 GC의 대상에서 제외되는 경우는 지역변수를 참조하는 내부함수가 외부로 전달되는 경우가 유일합니다.


이렇게 되면 클로저를 새롭게 정의할 수 있겠습니다.

클로저란 외부함수의 변수를 참조하는 내부함수가 외부로 전달될 경우 외부함수가 종료된 이후에도 참조한 변수가 사라지지 않는 현상입니다.

5-2. 클로저와 메모리 관리

클로저는 객체지향과 함수형 모두를 아우르는 매주 중요한 개념입니다!
메모리 소모는 클로저의 본질적인 특성입니다. 이에 따라 이를 정확히 이해하고 잘 활용할 수 있어야겠습니다.

메모리 관리 방법은 이렇습니다. 클로저는 어떤 필요에 의해 의도적으로 함수의 지역변수를 GC 대상이 되지 않게 함으로써 메모리를 소모합니다. 그렇다면, 그 필요성이 사라지 시점에서는 더는 메모리를 소모하지 않게 하면 됩니다. 참조 카운트를 0으로 하면 됩니다.

그렇다면 참조 카운트를 0으로 하는 방법에는 무엇이 있을까요?
바로 식별자에 참조형이 아닌 기본형 데이터(null 또는 undefined)를 할당하면 됩니다. 코드를 보시죠!


outer 식별자의 inner 함수 참조를 끊음

var outer = (function () {
  var a = 1;
  var inner = function () {
    return ++a;
  }
  return inner;
})();

console.log(outer());		
console.log(outer());
outer = null;

정리

결국, 클로저는 이 개념만 제대로 이해하고 있으면 됩니다.

클로저란 외부함수의 변수를 참조하는 내부함수가 외부로 전달될 경우 외부함수가 종료된 이후에도 참조한 변수가 사라지지 않는 현상입니다!

클로저란 외부함수의 변수를 참조하는 내부함수가 외부로 전달될 경우 외부함수가 종료된 이후에도 참조한 변수가 사라지지 않는 현상입니다!!

클로저란 외부함수의 변수를 참조하는 내부함수가 외부로 전달될 경우 외부함수가 종료된 이후에도 참조한 변수가 사라지지 않는 현상입니다!!!

위의 글은 온전히 학습 목적을 위해 작성한 글입니다. 위 내용의 모든 지적 재산권,저작권은 코어 자바스크립트 저자에게 있으며, 무단 전재 및 재배포를 금합니다.

profile
내실 있는 프론트엔드 개발자가 되기 위해 오늘도 최선을 다하고 있습니다.

0개의 댓글