클로저는 자바스크립트 개념에서 난해하기로 유명한 개념 중 하나이다.
딥하게 이해하기에 너무 난해하니 핵심 키워드로 함수가 선언된 렉시컬 환경으로 이해한다.
const x = 1;
function outFn(){
const x = 10;
function innerFn(){
console.log(x); // 10
}
innerFn();
}
outFn();
전역 변수 x가 선언되고 outFn() 안에 innerFn() 내부 함수로 선언되었기 때문에
outFn() 변수를 참조한다.
자바스크립트는 함수를 어디서 호출했는지가 아닌, 함수를 어디에서 정의했는지에 따라 상위 스코프가 정해진다. 이를 렉시컬 스코프라 한다.
const x = 1;
function outFn(){
const x = 10;
const inner = function () { console.log(x); }
return inner;
}
// outFn 호출하면 inner 반환
// 그리고 outFn 실행 컨텍스트는 실행 컨텍스트 스텍에서 팝되어 제거
const innerFn = outFn();
innerFn(); // 10
외부 함수보다 중첩 함수가 더 오래 유지되는 경우, 중첩 함수는 이미 생명 주기가 종료한 외부 함수의 변수를 참조할 수 있다. 이러한 중첩 함수를 클로저라 한다. (죽은애 계속 참조🥺)
outFn()는 반환하고 생명 주기가 종료 후 실행 컨텍스트 스텍에서 제거되지만 outFn()의 렉시컬 환경까지 소멸하는 것은 아니다.
반대로 이야기하면 중첩 함수에서 상위 스코프를 참조하지 않으면, 메모리 낭비로 인해 자바스크립트에서는 상위 스코프를 기억하지 않는다. 따라서 중첩 함수이지만 스코프 참조가 없다면 클로저가 아니다.
클로저는 중첩 함수가 상위 스코프의 식별자를 참조하고 있고, 중첩 함수가 외부 함수보다 생명주기가 길때 클로저라 한정한다.
클로저는 상태를 안전하게 보호하고 특정 함수에게만 상태 변경 및 안전하게 유지하기 위해 사용한다. 상태를 안전하게 변경하고 유지할 수 있는 예제를 살펴보자
// 카운트 상태 변경
const increase = (function()) {
let num = 0;
// 클로저
return function () {
return ++num;
};
}());
console.log(increase()); // 1
console.log(increase()); // 2
즉시실행 함수는 실행되고 소멸되지만 반환된 클로저 때문에 increase 변수가 호출될 때마다 num은 증가되고, 렉시컬 환경을 기억하고 있기 때문에 어디서 호출하든지 참조하고 변경할 수 있다.