클로저(closure)는 자바스크립트 고유의 개념이 아니라 함수형 프로그래밍 언어에서 등장하는 보편적인 특성이다. 개념이 아니기 때문에 ECMAScript 명세서에도 클로저의 정의를 다루지 않고 있어 클로저에 대한 다양한 정의와 설명이 존재한다.
자신을 내포하는 함수의 컨텍스트에 접근할 수 있는 함수
- 더글라스 크록포드 (자바스크립트 핵심 가이드)함수가 특정 스코프에 접근할 수 있도록 의도적으로 그 스코프에서 정의하는 것
- 에딘 브라운 (러닝 자바스크립트)
함수를 선언할 때 만들어지는 유효범위가 사라진 후에도 호출할 수 있는 함수
- 존 레식 (자바스크립트 닌자 비급)
이미 생명 주기상 끝난 외부 함수의 변수를 참조하는 함수
- 송형주 고형준 (인사이드 자바스크립트)
자유변수가 있는 함수와 자유변수를 알 수 있는 환경의 결합
- 에릭 프리먼 (Head First Javascript programming)
로컬 변수를 참조하고 있는 함수 내의 함수
- 야마다 요시히로 (자바스크립트 마스터북)
자신이 생성되 때의 스코프에서 알 수 있었던 변수들 중 언젠가 자신이 실행될 대 사용할 변수들만을 기억하여 유지시키는 함수
- 유인동 (함수형 자바스크립트 프로그래밍)
클로저는 함수와 함수가 선언된 어휘적 환경의 조합이다
- MDN
이처럼 많은 설명과 정의가 존재하지만 막상 그 의미를 이해하기는 쉽지않다. MDN에서는
라고 소개한다. 직역하면 "클로저는 함수와 그 함수가 선언될 당시의 lexical enviroment의 상호관계에 따른 현상" 정도가 된다.
그림에서처럼 Outer내부에서 선언된 내부함수 Inner의 실행 컨텍스트가 활성화된 시점에는 Inner의 outerEnvironmentReference가 참조하는 대상인 Outer의 LexicalEnviroment에 접근이 가능하다. Outer함수에서는 Inner함수에 선언한 변수에 접근할 수 없지만 Inner함수에서는 Outer에서 선언한 변수에 접근이 가능하다. (스코프 체인) Inner함수에서 Outer함수의 변수를 참조하는 경우에 한해서 LexicalEnviroment의 상호관계에 의미가 생긴다.
예제 1
var outer = function() {
var a = 1;
var inner = function() {
console.log(++a;)
};
inner();
}
outer(); // 2
위의 예제에서 outer함수에서 변수 a를 선언했고, outer함수의 내부함수인 inner 함수에서 a의 값을 1만큼 증가시킨 다음 출력한다.
예제 2
var outer = function () {
var a = 1;
var inner = function () {
return ++a;
};
return inner;
};
var outer2 = outer();
console.log(outer2()); // 2
console.log(outer2()); // 3
예제2는 예제1과 다르게 inner 함수 자체를 반환했다.
종합해보면 클로저란 어떤 함수 A에서 선언한 변수 a를 참조하는 내부함수 B를 외부로 전달할 경우 A의 실행 컨텍스트가 종료된 이후에도 변수 a가 사라지지 않는 현상을 말한다. (가비지 컬렉팅되지 않기 때문에)
'메모리 누수'라는 표현은 어떤 값의 참조 카운트가 0이 되지 않아 가비지 컬렉터의 수거 대상이 되지 않는 경우에 발생한다. 클로저는 어떤 필요에 의해 의도적으로 함수의 지역변수가 메모리를 소모하도록 함으로써 발생한다. 그 필요성이 사라진 시점에 참조 카운트를 0으로 만들어 주면 가비지 컬렉터가 수거하고 메모리 누수를 막을 수 있다. (참조 카운트를 0으로 만드는 방법은 식별자에 기본형 데이터(null, undefined)를 할당하면 된다.)
// setInterval에 의한 클로저의 메모리 해제
(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);
})();
참고 : 코어 자바스크립트(정재남)