클로저
“A closure is the combination of a function and the lexical environment within which that function was declared.”
클로저는 함수와 그 함수가 선언된 렉시컬 환경과의 조합이다.
뭔소린가 싶다. 키워드는 **“함수가 선언된 렉시컬 환경”**이란다.
const x = 1;
function outerFunc() {
const x = 10;
function innterFunc() {
console.log(x); // 10
}
innerFunc();
}
innerFunc는 상위 스코프 outerFunc에 있는 변수 x에 접근한다. 만약 innerFunc가 outerFunc의 중첩함수가 아니라면, innerFunc는 outerFunc의 변수 x에 접근할 수 없고, 대신에 전역 변수인 1을 출력할 것이다.
const x = 1;
function outerFunc() {
const x = 10;
innerFunc();
}
function innerFunc() {
console.log(x); // 1
}
outerFunc(); // 1
이같은 현상이 발생하는 이유는 자바스크립트가 렉시컬 스코프를 따르는 프로그래밍 언어이기 때문이다.
렉시컬 스코프 : 함수가 정의된 위치/시점에서 상위 스코프가 결정됨
렉시컬 스코프
const x = 1;
function foo() {
const x = 10;
bar();
}
function bar() {
console.log(x);
}
foo(); // 1
bar(); // 1
렉시컬 환경에서는 함수가 정의된 위치에서 상위 스코프가 결정된다. 함수 bar의 상위는 전역이므로, bar함수 내부의 x는 전역 변수 x에 접근하게 된다. 함수를 어디서 호출하는지는 함수의 상위 스코프 결정에 어떠한 영향도 주지 못한다. 즉, 함수의 상위 스코프는 함수를 정의한 위치에 의해 정적으로 결정되고 변하지 않는다.
스코프의 실체는 실행 컨텍스트의 렉시컬 환경이다. 이 렉시컬 환경은 자신의 “외부 렉시컬 환경에 대한 참조 Outer Lexical Environment Reference”를 통해 상위 렉시컬 환경과 연결된다. 이것이 바로 스코프 체인이다.
*실행 컨텍스트의 생성과 식별자 검색 과정 참조
따라서 “함수의 상위 스코프를 결정한다”는 “렉시컬 환경의 외부 렉시컬 환경에 대한 참조에 저장할 참조값을 결정한다”는 것과 같다. 렉시컬 환경의 “외부 렉시컬 환경에 대한 참조”에 저장할 참조값이 바로 상위 렉시컬 환경에 대한 참조이며, 이것이 상위 스코프이기 때문이다. 이 개념을 반영해서 다시 한번 렉시컬 스코프를 정의해 보면 다음과 같다.*
클로저와 렉시컬 환경
const x = 1;
//1
function outer() {
const x = 10;
const inner = function () {console.log(x);} // 2
return inner;
}
// outer 함수를 호출하면 중첩 함수 inner를 반환한다.
// 반환 이후 outer 함수의 실행 컨텍스트는 실행 컨텍스트 스택에서 팝되어 제거된다.
const innerFunc = outer(); // 3
innerFunc(); // 4 - 10 출력
outer();
// f console.log(x) 반환
outer 함수를 호출하면 outer 함수는 중첩 함수 inner를 반환하고, 생명 주기를 마감한다. 즉 outer 함수의 실행이 종료되면 outer 함수의 실행 컨텍스트는 실행 컨텍스트 스택에서 제거된다. 이때 ㅎouter함수의 지역 변수 x와 변수 값 10을 저장하고 있던 outer 함수의 실행 컨텍스트가 제거되었으므로, outer 함수의 변수 x (10) 또한 새염ㅇ 주기를 마감한다. 따라서 outer 함수의 지역 변수 x 는 더이상 유효하지 않게 되어 x 변수에 접근할 수 있는 방법은 달리 없어 보인다. 그러나 위 코드의 결과는 10을 출력한다. 이미 생명 주기가 종료되어 실행 컨텍스트 스택에서 제거된 outer 함수의 지역 변수 x가 다시 부활이라도 한 듯이 동작하고 있다.
이처럼 외부 함수(outer)보다 중첩 함수(inner)가 더 오래 유지되는 경우 중첩 함수는 이미 생명 주기가 종료한 외부 함수의 변수(const x=10)를 참조할 수 있다. 이러한 중첩 함수(inner)를 클로저라고 부른다.