클로저는 자바스크립트 고유의 개념이 아니다. 함수를 일급객체로 취급하는 함수형 프로그래밍 언어에서 공통적으로 사용되는 특성이다. 따라서 ECMAScript 사양에 정의되어있지 않다. 대신 MDN에 따른 클로저의 정의는 다음과 같다.
클로저는 함수와 그 함수가 선언된 렉시컬 환경과의 조합이다.
렉시컬 환경? 렉시컬 환경과의 조합? 처음 읽을 때는 무슨 말인지 도통 이해가 가질 않는다. 정의를 이해하기 위해 먼저 아래 코드들을 살펴보자
// 1. inner 함수가 outer 함수 바깥에서 정의된 경우
const x = 1;
function outer() {
const x = 10;
inner();
}
function inner() {
console.log(x);
}
outer(); // 1
// 2. inner 함수가 outer 함수 내부에서 정의된 경우
const x = 1;
function outer() {
const x = 10;
function inner() {
console.log(x);
}
inner();
}
outer(); // 10
코드를 실행해보면, inner
함수가 정의된 위치에 따라 참조하는 변수 x
가 달라지는 것을 확인할 수 있다.
outer
함수 외부에서 inner
함수를 선언했을 때는 outer
외부의 변수 x
를 참조하고,
outer
함수 내부에서 inner
함수를 선언했을 때는 outer
내부의 변수 x
를 참조한다.
자바스크립트는 정적 스코프를 따르는 언어이다. (대부분의 언어가 그렇다) 정적 스코프를 따르는 언어는 함수가 선언된 위치에 따라 상위 스코프가 정해진다. 즉 어디에 함수를 정의했는지에 따라서 상위 스코프가 정해진다.
또한 자바스크립트에서 스코프라는 개념은 렉시컬 환경
을 통해 구현된다.
즉 렉시컬 환경
이 스코프의 실체라고 할 수 있다.
MDN에 의한 클로저의 정의를 다시 한번 보자
클로저는 함수와 그 함수가 선언된 렉시컬 환경과의 조합이다.
위에서 말했듯이 정적 스코프를 따르는 언어는 함수가 선언된 위치에 따라 상위 스코프(상위 렉시컬 환경)가 정해진다. 따라서 정의를 쉽게 바꾸면 다음과 같다.
클로저는 함수와 그 함수의 상위 스코프와의 조합이다.
const x = 1;
function outer() {
const x = 10;
const inner = function () {
console.log(x);
}
return inner;
}
const inner = outer(); // ...(*)
inner(); // 10
위 코드는 outer
함수 외부와 내부에 각각 다른 값을 가지는 변수 x
를 선언하고,
중첩함수 inner
로 하여금 console.log
를 통해 x
를 출력하게 만든다.
(*)
에서 outer
함수의 호출 후 outer
함수의 실행이 끝나면 outer
함수의 생명주기도 종료되어
outer
함수 내부 변수 x
에 접근할 수 없을 것 처럼 보인다.