클로저

SeungMin·2022년 11월 3일
0

JAVA SCRIPT STUDY

목록 보기
7/9

클로저란?

어떤 함수A 내부에 선언된 내부함수B 에서 A의 지역변수를 사용하는 경우 발생한다.

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

위의 코드의 흐름을 축약하면

  • 전역 컨텍스트가 콜스택에 쌓인다
  • outer 함수가 호출되며 outer실행 컨텍스트가 콜스택에 쌓인다
  • outer 함수가 실행된다.
  • inner 함수가 호출되며 inner실행 컨텍스트가 콜스택에 쌓인다.
  • inner 함수가 실행되며 outer 스코프에서 선언된 변수 a를 참조한다.
  • inner 실행 컨텍스트가 콜스택에서 제거된다.
  • outer 실행 컨텍스트가 콜스택에서 제거된다.
  • 전역 컨텍스트가 콜스택에서 제거되며 종료된다.

이때 inner에서 a를 참조하며 inner의 내부 스코프에 a를 찾지 못하므로
innerouterEnvironmentReference를 통해서
외부 스코프인 outerLexicalEnvironment를 참조하고
그 요소인 environmentRecord의 식별자 정보를 참조하여 최종적으로 outer의 a를 찾는다.

하지만 흐름에 따라서 실행 컨텍스트가 순차적으로 제거되고
inner 실행 컨텍스트가 제거되면 outer 실행 컨텍스트의 a를 참조 카운트가 0이되어
outer의 실행이 끝나면 자연스럽게 outer 실행 컨텍스트도 제거되고
GC(Garbege Collector)의 수집 대상이 된다.


그럼 의도적으로 클로저를 발생시켜보자

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

위의 코드는 상단의 코드와 비슷하지만 다르다

outer 함수의 실행 결과로 내부에서 선언된 inner 함수 자체를 return했다.

따라서 outer2inner함수가 할당되었고, 따라서 outer2를 실행하게 되면
outer에서 선언된 함수 내부의 a를 참조할 가능성이 생긴다.

이때 참조할 가능성이 생겼기 때문에 outer 실행 컨텍스트의 참조 카운트는 0이 아니다.

따라서 outer2의 실행이 종료되기 전 (inner함수의 실행 컨텍스트가 콜스택에서 제거되기 전)
까지는 콜스택에 outer의 실행 컨텍스트가 참조되기 위해 유령처럼 남아있게 된다.

아래는 설명에 대한 간략한 흐름도 이다.

이런 클로저를 정의한 몇몇 문장이 있다.

  • 함수를 선언할 때 만들어지는 유효범위가 사라진 후에도 호출할 수 있는 함수
  • 이미 생명 주기가 끝난 외부 함수의 변수를 참조하는 함수
  • 자신이 생성될 때의 스코프에서 알 수 있었던 변수들 중 언젠가 자신이 실행될 때 사용할 변수들만을 기억하여 유지시키는 함수

즉 위의 예시에서는 inner가 클로저에 해당한다.

클로저를 한 마디로 정의하면
어떤 함수A 에서 선언한 변수 a를 참조하는 내부함수B를 외부로 전달할 경우
A의 실행 컨텍스트가 종료된 이후에도 변수 a가 사라지지 않는 현상
입니다.

그밖에 return 없이 클로저가 발생하는 경우는
지역변수를 참조하는 내부함수를 외부에 전달하면서 발생하는데

대표적인 예로는 setInterval , setTimeout , eventListener 등이 있다.

클로저의 메모리 관리

클로저는 앞서 언급했듯이 소멸했어야 할 변수를 참조하고 있는 상황이기 때문에
그만큼 메모리를 소모한다.

하지만 이건 메모리 누수와는 조금 다르다.

클로저를 의도적으로 사용했다면
말 그대로 개발자의 의도대로 동작했기 때문에 메모리 누수 라고는 할 수 없다.

그저 클로저의 특징일 뿐이다.

그럼 코드의 동작중 클로저의 필요성이 사라질 경우
참조 카운트를 의도적으로 0으로 만드는 방법은 뭘까


간단하다.

클로저의 식별자에 참조형이 아닌 기본형 데이터 (보통null 또는 undefined) 를
할당하면 된다.

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

클로저의 동작이 끝난 후 null 을 할당하여 참조 카운트를 0으로 만든다
따라서 outer의 참조 카운트가 0이 되므로 GC의 수거 대상이 된다.

접근 권한 제어 (정보 은닉)

대표적인 접근 권한 제어의 개념으로 public , private이 있다.

이를 클로저를 이용해서 구현할 수 있는데

var outer = function(){

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

위의 코드를 보면 outer2outer의 리턴값인 inner 함수를 할당했기 때문에
outer의 지역변수 a를 외부에서도 읽을 수 있게 되었다. : public

profile
공부기록

0개의 댓글