클로저
- 클로저는 함수와 그 함수가 선언된 렉시컬 환경과의 조합이다
const x = 1;
function name() {
const x = 10;
function innerFunc() {
console.log(x);
}
innerFunc();
}
name(); // 10
- name 함수 안에 중첩 함수 innerFunc가 정의되고 호출되었다.
- 이때 innerFunc의 상위 스코프는 외부 함수 name의 스코프다. 이로 인해 innerFunc는 x에 접근이 가능하다.
렉시컬 스코프
- 자바스크립트 엔진은 함수를 어디서 호출했는지가 아니라 함수를 어디에 정의했는지에 따라 상위 스코프를 결정하는데 이를 렉시컬 스코프 라고 한다
const x = 1;
function foo() {
const x = 10;
bar()
}
function bar() {
console.log(x);
}
foo(); // 10
- 위 예제를 보면 foo 함수와 bar 함수 모두 전역에서 정의 되었다. foo 함수와 bar 함수의 상위 스코프는 전역이다 .
- 스코프의 실체는 실행 컨텍스트의 렉시컬 환경이다. 이 렉시컬 환경은 자신의 외부 렉시컬 환경에 대한 참조를 통해 상위 렉시컬 환경과 연결된다. 이것이 스코프 체인이다
- 랙시컬 환경의 외부 렉시컬 환경에 대한 참조애 저장할 값, 상위 스코프에 대한 참조는 함수 정의가 평가되는 시점에 함수가 정의된 환경에 의해 결정된다. 이것이 렉시컬 스코프다.
함수 객체의 내부 슬롯[[Enviornment]]
- 함수는 자신의 내부 슬롯에 자신이 정의된 환경, 즉 상위 스코프 참조를 저장한다.
- 함수 정의가 평가되어 함수 객체를 생성할 때 정의된 환경에 의해 결정된 상위 스코프에 대한 참조를 [[Enviornment]] 내부 슬롯에 저장한다. 이 때 저장된 상위 스코프의 참조는 현재 실행중인 실행 컨텍스트 렉시컬 환경을 가르킨다.
- 함수가 평가되어 함수 객체를 실행하는 시점은 함수가 정의된 환경, 상위 함수가 평가 또는 실행되는 시점이고 현재 실행중인 실행 컨텍스트는 상위 함수의 실행 컨텍스트이기 때문이다.
- 에를 들어 전역에서 정의된 함수의 경우 전역 코드가 평가되는 시점에 함수객체를 생성하는데, 이때 생성된 함수 객체 내부 슬롯[[Enviornment]] 에는 함수 정의가 평가되는 시점, 외부 렉시컬 환경의 참조가 저장된다.
- [[Enviornment]]에 저장된 것이 바로 상위 스코프다. 또한 자신이 호출되었을 때 생성될 함수 렉시컬 환경의 외부 렉시컬 환경에 대한 참조에 저장될 참조값이다.
const x = 1;
function foo() {
const x = 10;
bar()
}
function bar() {
console.log(x);
}
foo(); // 10
- 위 예제에서 foo, bar 함수 모두 전역에서 함수 선언문으로 정의되었다.
- foo, bar 함수는 전역 코드가 평가되는 시점에 평가되어 함수 객체를 생성하고 전역 객체 window의 메서드가 된다.
- 이때 [[Enviornment]]에는 전역 렉시컬 환경의 참조가 저장된다.
- 함수가 호출되면 함수 내부로 코드의 제어권이 이동한다. 그리고 함수 코드의 평가가 시작된다.
- 이 때 함수 렉시컬 환경의 구성 요소인 외부 렉시컬 환경에 대한 참조에는 함수 객체의 내부 슬롯 [[Enviornment]]에 저장된 렉시컬 환경의 참조가 할당된다.
클로저와 렉시컬 환경
const x = 1;
function outer() {
const x = 10;
const inner = function () {console.log(x);}
return inner;
}
const innerFunc = outer();
innerFunc()
- outer 함수를 호출하면 outer 함수는 중첩 함수 inner 함수를 반환하고 생명주기를 마감한다.
- outer 함수는 실행이 종료되면 outer 함수의 실행 컨텍스트는 실행 컨텍스트 스택에서 제거된다. 이로 인해 x 와 변수 10을 저장하고 있던 outer 함수가 종료되었으므로, 지역변수 x 또한 생명주기를 마감한다.
- 하지만 inner 함수는 x 변수에 접근해 값을 사용이 가능한데, 이처럼 외부 함수보다 중첩함수가 더 오래 유지되는 경우 중첩함수는 이미 생명주기가 종료한 외부함수의 변수를 참조할 수 있다. 이러한 중첩 함수를 클로저라고 한다.
- 자바스크립트에서 모든 함수는 자신의 상위 스코프를 기억한다고 한다. 이로인해 상위 스코프의 식별자를 참조하고 바인딩된 값을 변경할 수 있게 된다.
- inner 함수는 자신이 평가될 떄 정의된 위치에 의해 결정된 상위 스코프를 [[Eniviornment]]에 저장한다.
- outer 함수의 실행 컨텍스트는 스택에서 제거되지만, outer 함수의 렉시컬 환경까지 소멸되는 것은 아니다.
- outer 함수의 렉시컬 환경은 inner 함수의 [[Enviornment]] 내부 슬롯에 의해 참조되고 있고 inner 함수는 전역변수 innerFunc에 의해 참조되고 있으므로 가비지 컬렉션의 대상이 되지 않기 떄문이다.
클로저
중첩함수가 상위 스코프의 식별자를 참조할 수 있고 중첩 함수가 외부 함수보다 더 오래 유지된다
- 클로저에 의해 참조되는 상위 스코프 변수를 자유변수라 한다.