[240401] 23장 클로저 (1)

23장 클로저 (1)

클로저는 자바스크립트 고유의 개념이 아님

일급 객체로 취급하는 함수형 프로그래밍 언어 (하스켈, 리스프, 얼랭 , 스칼ㄹ라 등) 에서 사용되는 중요한 특성

→ 고유의 개념이 아니므로 클러저의 정의가 ECMAScript 사양에 등장하지 않음

→ MDN 왈 : 클로저는 함수와 그 함수가 선언된 렉시컬 환경과의 조합이다.

렉시컬 스코프

  • 자바스크립트 엔진은 함수를 어디서 호출했는지가 아니라 함수를 어디에 정의했는지에 따라 상위 스코프를 결정한다. 이를 렉시컬 스코프(정적 스코프)라고 함
  • 렉시컬 환경의 “외부 렉시컬 환경에 대한 참조”에 저장할 참조값, 즉 상위 스코프에 대한 참조는 함수 정의가 평가되는 시점에 함수가 정의된 환경(위치)에 의해 결정된다.

함수 객체의 내부 슬롯 [[Environment]]

  • 함수는 정의된 환경과 호출되는 환경이 다를 수 있는데, 렉시컬 스포크가 가능하려면 함수는 환경에 상관없이 상위 스코프를 기억해야함
  • 이를 위해 내부 슬롯 [[Environment]] 에 자신이 정의 된 환경, 즉 상위 스코프의 참조를 저장함
  • 함수 정의가 평가되어 객체를 생성할 때 저장함
  • [[Environment]] 에는 현재 실행 중인 실행 컨텍스트의 렉시컬 환경의 참조가 저장됨 ( 추후 외부 렉시컬 환경에 대한 참조에 저장될 값임 )
const x = 1;

function foo() {
	const x = 10;
	
	// 상위 스코프는 함수 정의 환경(위치)에 따라 결정된다.
	// 함수 호출 위치와 상위 스코프는 아무런 관계가 없다.
	bar();
}

// 함수 bar는 자신의 상위 스코프, 즉 렉시컬 환경을 [[Environment]] 에 저장하여 기억한다.
function bar() {
	console.log(x);
}

foo();
bar(); 

클로저와 렉시컬 환경

const x = 1;

function outer() {
	const x = 10; 
	const inner = function () { console.log(x); };
	return inner;
}

// outer 함수를 호출하면 중첩 함수 inner 를 반환한다.
// outer 함수의 실행 컨텍스트는 실행 컨텍스트 스택에서 팝되어 제거된다.
const innerFunc = outer();
innerFunc(); // 10 

위 코드를 보면 지역 변수 10을 저장하고 있던 outer 함수의 실행 컨텍스트가 제거되었으므로 생명 주기를 마감한다. 하지만 예제에서는 x 가 다시 부활이라도 한 듯 동작중임

→ 외부 함수보다 중첩 함수가 더 오래 유지되는 경우 중첩 함수는 이미 생명 주기가 종료한 외부 함수의 변수를 참조할 수 있고, 이러한 중첩 함수를 클로저라고 부름

→ outer 함수의 실행 컨텍스트는 스택에서 제거되지만 outer 함수의 렉시컬 환경까지 소멸하는 것은 아님

이론적으로는 모든 함수가 상위 스코프를 기억하므로 클로저이지만, 일반적으로 모든 함수를 클로저라고 하지 않음

  1. 상위 스코프의 어떤 식별자도 참조하지 않는 함수는 클로저가 아니다.
  2. 중첩이지만 외부 함수보다 일찍 소멸하면 클로저의 본질에 부합하지 않는다.

자유변수

  • 클로저에 의해 참조되는 상위 스코프의 변수
  • 엔진 최적화가 잘 되어 있어 클로저가 참조하고 있지 않은 식별자는 기억하지 않는다.
  • 클로저는 자바스크립트의 강력한 기능이고 적극 활용해야함

Categories:

Updated:

Leave a comment