클로저는 함수와 그 함수가 선언된 어휘적 환경(lexical environment)의 조합이다. 이를 통해 함수는 자신이 선언될 때의 환경에서 알 수 있었던 변수 중 필요한 변수들을 기억하고 이를 통해 독립적인 환경을 생성한다. 이는 상태를 안전하게 변경하고 유지한다.
간단한 클로저의 예시를 살펴보자
function outerFunction() {
const outer = 'I see the outer variable';
function innerFunction() {
console.log(outer);
}
return innerFunction;
}
const innerFunc = outerFunction();
innerFunc(); // 'I see the outer variable'
위의 코드에서 outerFunction은 안에 outer라는 변수를 가지고 있고, innerFunction이 그 안에서 정의된다. innerFunction은 outer에 접근할 수 있다. outerFunction은 innerFunction을 반환하고 있으므로, innerFunc 변수에는 innerFunction이 할당되고 마지막으로 innerFunc가 호출될 때, "나는 바깥 변수를 보고 있어"라는 문자열을 출력한다. 이는 innerFunction이 outer 변수를 기억하고 있기 때문에 외부에서 접근할 수 없도록 보호하는 것이 클로저의 핵심 개념 중 하나이다.
클로저는 자바스크립트에서 많이 사용되는 패턴 중 하나이다. 예를 들면, 이벤트 핸들러에서 변수를 보호하기 위해 클로저를 사용하거나, 비동기적인 함수에서 변수를 유지하기 위해 클로저를 사용하는 등 다양한 상황에서 활용된다.
함수 객체의 내부 슬롯 [[Environment]]은 함수가 선언될 때의 환경을 말한다. 이 환경은 스코프 체인을 구성하며, 함수가 호출될 때마다 해당 환경을 참조한다.
자바스크립트에서 함수는 일급 객체로 취급되기 때문에 다른 객체와 마찬가지로 속성 값을 가질 수 있다. 이러한 속성 중에서도 가장 중요한 것이 [[Environment]] 내부 슬롯이다. [[Environment]] 내부 슬롯은 함수가 선언될 당시의 렉시컬 환경(Lexical Environment)을 가리키며, 스코프 체인을 구성하는 중요한 역할을 한다.
스코프 체인이란 변수를 검색할 때 참조하는 중첩된 렉시컬 환경의 목록이다. 예를 들어, 함수 내부에서 변수를 참조하면 자바스크립트 엔진은 먼저 함수 내부의 렉시컬 환경에서 변수를 검색하고, 만약 해당 변수를 찾지 못하면 외부 렉시컬 환경에서 변수를 검색한다. 이렇게 스코프 체인을 통해 변수를 검색하면서, [[Environment]] 내부 슬롯이 참조하는 렉시컬 환경 내부에 있는 변수에 접근할 수 있다.
클로저란 함수와 그 함수가 선언될 때의 렉시컬 스코프 사이의 조합으로, 함수가 자신이 생성될 때의 스코프에서 알 수 있었던 변수들 중 언젠가 자신이 실행될 때 사용할 변수들만 기억하고 있다. 이는 함수가 수행될 때 독립적인 환경을 만들어 줄 수 있고, 이를 통해 상태를 안전하게 변경하고 유지할 수 있다.
따라서, [[Environment]] 내부 슬롯은 함수의 동작 방식을 결정하는 중요한 역할 한다. 함수가 호출될 때마다 해당 환경을 참조하는 것을 통해, 클로저를 구현하고 상태를 안전하게 유지할 수 있다. 이를 통해 자바스크립트에서 함수를 이용한 다양한 기능들을 구현할 수 있다.
클로저와 가비지 컬렉션
<aside> 💡 가비지 컬렉션 (Garbage Collection)
JavaScript는 메모리 관리를 자동으로 한다. 이를 위해 사용되는 기술 중 하나가 가비지 컬렉션이다. 객체가 더 이상 사용되지 않을 때, 즉 어떠한 참조도 가리키지 않을 때 해당 객체는 메모리에서 제거된다.
</aside>
클로저는 외부 함수의 변수에 대한 참조를 유지한다. 따라서 클로저가 존재하는 한, 클로저에 의해 참조되는 변수나 객체는 메모리에서 해제되지 않는다. 이는 때로는 의도치 않게 메모리 누수(memory leak)의 원인이 될 수 있다.