클로저
는 자바스크립트 고유의 개념이 아니라 함수를 일급 객체
로 취급하는 함수형 프로그래밍 언어에서 사용되는 중요한 특성이다.
클로저는 자바스크립트 고유의 개념이 아니므로 ECMAScript 명세에 클로저의 정의가 등장하지 않는다.
클로저에 대해 MDN은 아래와 같이 정의한다.
함수와 함수가 선언되었을 때 렉시컬 환경(Lexical Environment)과의 조합
함수가 선언될 때 자동으로 생성되는 렉시컬 환경에 대한 설명이다.
이러한 렉시컬 환경은 스코프체인(Scope Chain)을 형성하게 되는데, 스코프 체인은 함수가 선언될 때의 모든 변수와 함수를 포함하는 렉시컬 스코프(Lexical Scope)를 형성한다.
외부 함수가 실행되고 반환된 후에도 외부 함수 범위 내의 함수에 체이닝을 할 수 있는 함수이다.
정보를 은닉하기 위해 주로 사용한다.
function outer() {
const outerVal = '외부 변수';
function inner() {
console.log(outerVal);
}
return inner;
}
const innerExecute = outer();
innerExecute(); // 외부 변수
outer
는 inner
를 return한다.
innerExecute
에는 outer
에서 반환한 inner
함수가 할당되게 된다.
innerExecute
를 호출할 때마다, outer
에서 정의한 outerVal
을 출력하게 된다.
이때 inner
함수는 outer
함수의 스코프체인을 유지하게 되는데, 이러한 특성을 이용하여 클로저를 구현한 것이다.
이처럼 자신을 포함하고 있는 외부 함수보다 내부 함수가 더 오래 유지되는 경우, 외부 함수 밖에서 내부 함수가 호출되더라도 외부 함수의 지역 변수에 접근할 수 있는데(outerVal
), 이러한 함수를 클로저(Closure)라고 부른다.
자바스크립트의 함수는 숨김 프로퍼티인 [[Environment]]
를 이용해 자신이 어디서 만들어졌는지를 기억한다.
따라서 inner.[[Environment]]
엔 outerVal
이 있는 렉시컬 환경에 대한 참조가 저장된다. 호출 장소와 상관없이 함수가 자신이 태어난 곳을 기억할 수 있는 건 바로 이 [[Environment]]
프로퍼티 덕분이다. [[Environment]]
는 함수가 생성될 때 딱 한 번 값이 세팅되고 영원히 변하지 않는다.
클로저는 내부 함수가 외부 함수의 스코프에 접근할 수 있게 해주며, 외부 함수의 실행이 끝난 후에도 외부 함수의 변수에 접근할 수 있게 한다.
이러한 특성 때문에 클로저는 모듈 패턴, 콜백 함수, 고차 함수 등 다양한 곳에서 활용된다.
이는 클로저를 통해 생성된 함수는 각자의 독립적인 변수 스코프를 가지게 되므로, 데이터의 안전성과 무결성을 보장할 수 있기 때문이다.
다시 MDN 정의로 돌아가 보자.
클로저는 함수와 그 함수가 선언됐을 때의 렉시컬 환경과의 조합이다.
위 정의에서 말하는 함수
란, 반환된 내부 함수를 의미하고 그 함수가 선언됐을 때의 렉시컬 환경
이란 내부 함수가 선언됐을 때의 스코프를 의미한다.
즉, 클로저는 반환된 내부 함수가 자신이 선언됐을 때의 환경(Lexical Environment)인 스코프를 기억하여 자신이 선언됐을 때의 환경(스코프) 밖에서 호출되어도 그 환경(스코프)에 접근할 수 있는 함수를 말한다.
이를 좀 더 간단히 말하면, 클로저는 자신이 생성될 때의 환경(Lexical Environment)를 기억하는 함수라고 말할 수 있다.
자바스크립트에서 함수는 실행될 때마다 각자의 실행 컨텍스트를 생성한다.
이 실행 컨텍스트는 함수의 호출 스택에 쌓이며, 함수의 실행이 완료되면 컨텍스트는 스택에서 제거된다.
하지만, 내부 함수가 외부 함수의 변수에 접근할 경우, 외부 함수의 실행 컨텍스트는 내부 함수에 의해 참조되므로 가비지 컬렉션의 대상이 되지 않는다.
이로 인해 외부 함수의 변수는 내부 함수가 존재하는 한 계속해서 접근 가능한 상태로 남게 된다.
왜냐하면 클로저는 함수와 그 함수의 렉시컬 환경을 함께 참조하기 때문에, 함수가 실행 컨텍스트에서 제거된 후에도 해당 환경에 있는 변수에 접근할 수 있기 때문이다.
클로저는 자바스크립트에서 매우 유용하게 사용된다.
예를 들어, 클로저는 데이터를 은닉하고 캡슐화하는 모듈 패턴을 구현하는 데 사용될 수 있다.
또한, 클로저는 콜백 함수와 이벤트 핸들러에서 특정 데이터를 참조해야 할 때, 그 데이터를 안전하게 보관하는 용도로 사용될 수 있다.
왜냐하면, 클로저를 통해 생성된 함수는 각자의 독립적인 변수 스코프를 가지므로, 외부에서의 무분별한 접근으로부터 데이터를 보호할 수 있기 때문이다.
클로저는 자신이 생성될 때의 환경을 기억해야 하므로 메모리 차원에서 손해를 볼 수 있다.
하지만, 클로저는 자바스크립트의 강력한 기능이므로 이를 적극 사용해야 한다.