[JavaScript] Closure

클로저

클로저란, 함수 하나와, 그 함수가 선언되었을 때 당시의 렉시컬 환경과의 조합이다. -mdn

자바스크립트에서 쓰이는 클로저의 개념은 자바스크립트만의 고유 개념이 아니다. 함수가 일급 객체로 쓰이는 함수형 프로그래밍 언어들에서 사용되는 중요한 개념이다.

함수가 선언되었을 때 렉시컬 환경이란 말은, 내부 함수가 선언되었을 때 당시의 스코프를 의미한다.

function outerFunc() {
  var x = 10;
  var innerFunc = function () { console.log(x); };
  innerFunc();
}

outerFunc(); // 10

outerFunc 내에서 내부함수 innerFunc가 선언되고, 호출되었다. 여기서 내부함수는 외부함수의 변수 x에 접근할 수 있다. 이 이유는 내부함수가 외부함수 안에서 선언되었기 때문이다.

스코프는 함수를 호출힐 때가 아닌 함수를 선언했을 당시에 어디에 선언되었는지에 따라서 결정된다. 렉시컬 스코핑이란 이를 말한다.

innerFunc가 선언된 위치는 outerFunc 내부이므로 outerFunc 스코프 안을 참조할 수 있게 되는 것이다. (자신이 속한 렉시컬 스코프(스코프 체인을 의미하는 것 같기도 함.) => 전역 스코프, outerFunc 스코프, 자신의 스코프)

클로저의 개념은 실행 컨텍스트를 알면 이해가 쉽다. 실행 컨텍스트 개념으로 다시 설명하면,

innerFunc가 호출되면 자신의 실행컨텍스트가 스택에 쌓이고, (call stack일까욤) VO, 스코프 체인, this 바인딩 객체까지 결정이 된다. 여기서 스코프 체인은 전역 스코프 - outerFunc의 스코프 - innerFunc(자신)의 스코프로 결정이 될 것이다. 즉, 여기서 말하는 스코프 체인이 렉시컬 스코프라고 할 수 있다!

내부함수인 innerFunc는 자신을 포함하고 있는 외부함수의 변수 x에 접근할 수 있는데, 이는 자신의 스코프에서 상위 스코프에 접근할 수 있다고 할 수 있다. 이럴 수 있는 이유는 실행컨텍스트의 스코프 체인이 리스트로 스코프를 저장하고 있기 때문이다. 이를 통해 자바스크립트 엔진이 순차적으로 스코프 체인을 활용하여 검색을 진행한다.

위 로직의 자바스크립트 엔진이 돌아가는 순서

  1. innerFunc 함수 스코프(자신의 스코프를 가리키는 AO) 내에서 변수 x를 검색한다.
  2. 검색이 실패하는 경우, innerFunc 함수를 포함하는 외부함수인 outerFunc의 스코프(outerFunc의 AO)에서 변수 x를 검색한다.
function outerFunc() {
  var x = 10;
  var innerFunc = function () { console.log(x); };
  return innerFunc;
}

/**
 *  함수 outerFunc를 호출하면 내부 함수 innerFunc가 반환된다.
 *  그리고 함수 outerFunc의 실행 컨텍스트는 소멸한다.
 */
var inner = outerFunc();
inner(); // 10

위의 경우, outerFunc는 innerFunc를 반환하고 콜스택(실행컨텍스트 스택)에서 제거되었다. 함수 outerFunc의 변수 x도 더이상 유효하지 않게 되었다. 하지만, 위 코드의 실행 결과는 변수 x의 값이 나온다. outerFunc의 라이프사이클이 종료되어 실행 컨텍스트 스택에서 제거되었음에도 해당 로직은 outFunc가 있는 듯이 동작한다.

이는 자신을 포함하고 있는 외부함수보다 내부함수가 더 오래 유지되는 경우에, 외부 함수 밖에서 내부 함수가 호출되더라도 외부 함수의 지역 변수에 접근할 수 있는 것을 보여주고 있다. 이러한 함수를 클로저라고 한다.

함수가 선언됐을 떄, 렉시컬 환경은 내부 함수가 선언되었을 때의 스코프를 의미한다. 즉, 클로저는 반환된 내부함수가 자신이 선언됐을 때의 환경(렉시컬 환경)인 스코프를 기억하여 해당 렉시컬 환경 밖에서 호출되어도 렉시컬 환경에 접근할 수 있는 함수가 클로저인 것이다.

클로저는 자신이 생성될 때의 환경을 기억하는 함수를 말한다.

클로저에 의해 참조되는 외부변수를 자유변수라고도 부른다. 클로저는 자유변수에 엮여있는(closed) 함수이다는 뜻이다.

외부함수가 종료되어 실행컨텍스트가 제거되어도, 외부함수 실행컨텍스트 내의 AO(변수, 함수 선언 등의 정보)는 내부함수에 의해 참조되는 한 유효하기 때문에 내부함수가 스코프 체인을 통해 참조할 수 있다.

외부함수가 이미 반환되었어도 외부함수 내의 변수는 이를 필요로하는 내부함수가 존재하는 경우 계속 유지된다. 이 때, 내부함수가 외부함수에 있는 실제 변수에 접근하게 된다.(스코프 체인 리스트를 통해 외부함수의 AO에 접근)

클로저의 활용

  1. 상태 유지

클로저가 가장 유용하게 사용되는 상황은 현재 상태를 기억하고 변경된 최신 상태를 유지하도록 해주는 부분에서이다.

  1. 전역 변수의 사용 억제

클로저를 사용하게되면, 지역 변수 상태 유지를 사용하여 전역 변수의 사용을 피할 수 있다.

  1. 정보 은닉

클래스 기반 언어의 private 키워드를 흉내내어 생성자 함수 내부에서 클로저로 메소드를 생성하여 사용할 수 있다.