💡 [코어자바스크립트-정재남, 위키북스]를 참고&인용하여 블로그를 작성했습니다.
클로저는 여러 함수형 프로그래밍 언어에서 등장하는 보편적인 특성이다. 자바스크립트만의 고유한 개념이 아니라 ECMAScript 명세에서도 클로저를 따로 정의하지 않는다.
MDN에서는 클로저에 대해 다음과 같이 설명한다.
A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function’s scope from an inner function. In JavaScript, closures are created every time a function is created, at function creation time.
클로저는 함수와 함수가 선언된 당시의 lexical environment의 조합이다. 즉, 클로저는 내부 함수가 외부 함수의 스코프에 접근할 수 있게 해주는 것을 의미한다. 자바스크립트에서 함수가 생성될 때마다 함수가 생성된 시점에 클로저가 함께 생성된다. 클로저를 이해하려면 자바스크립트가 어떻게 변수의 유효범위를 지정하는지(Lexical scoping)를 먼저 이해해야 한다.
function outer(x) {
return function inner() {
return x;
}
}
const two = outer(2);
console.log(two()) // 2
outer
함수 안에는 inner
함수가 있다. inner
함수는 outer
함수에 인자로 들어간 x
를 리턴한다.
outer
함수가 호출되면 outer
의 생명주기는 끝난다. 하지만, inner
함수는 x
를 참조한다.
위의 코드 예시처럼 외부 스코프를 기억하고 있는 것을 Closure(클로저) 라고 한다.
const outer = () => {
let a = 0;
const inner = () => {
console.log(a += 1)
};
inner();
};
outer(); // 1
outer
함수 안에서 변수 a
를 선언했고, outer
함수 내부에 inner
함수에서 a
의 값을 1 증가시킨 후 출력했다. inner
함수 내부에서 a
를 선언하지 않았기 때문에 environmentRecord
에서 값을 찾지 못하므로 outerEnvironmentReference
에 지정된 상위 컨텍스트인 outer
의 LexicalEnvironment
에 접근해서 다시 a
를 찾는다. outer
함수의 실행 컨텍스트가 종료되면 LexicalEnvironment
에 저장된 식별자들(a
, inner
)에 대한 참조를 지운다. 그러면 각 주소에 저장돼 있던 값들은 자신을 참조하는 변수가 하나도 없게 되므로 가비지 컬렉터의 수집 대상이 될 것이다.
위의 내용을 조금 바꿔서 다른 예시를 살펴보자
const outer = () => {
let a = 0;
const inner = () => {
return a += 1
};
return inner();
};
const outer2 = outer();
console.log(outer2); // 1
이번에도 inner
함수 내부에서 외부변수인 a
를 사용했다. 하지만 6번째 줄에서 inner
함수를 실행한 결과를 리턴하고 있으므로 결과적으로 outer
함수의 실행 컨텍스트가 종료된 시점에서는 a
변수를 참조하는 대상이 없어진다. 이 전의 예시와 마찬가지로 a
, inner
변수의 값들은 가비지 컬렉터의 수집 대상이 될 것이다. 역시 일반적인 함수 및 내부함수에서의 동작과 차이가 없다.
클로저란 어떤 함수에서 선언한 변수를 참조하는 내부함수를 외부로 전달할 경우 함수의 실행 컨텍스트가 종료된 후에도 해당 변수가 사라지지 않는 현상이다.
return
하는 경우 뿐 아니라 콜백으로 전달하는 경우도 포함된다.