24장 클로저

Boseong Choi·2023년 7월 7일
0

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

const x = 1;

function outerFuc() {
	const x = 10;

	function innerFunc() {
		console.log(x); // 10
	}
	
	innerFunc();
}

outerFunc();

outerFunc 함수 내부에서 중첩 함수 innerFunc가 정의되고 호출되었다. 이때 중첩 함수 innerFuc의 상위 스코프는 외부 함수 outerFunc의 스코프다. 따라서 중첩 함수 innerFunc 내부에서 자신을 포함하고 있는 외부 함수 outerFunc의 x 변수에 접근할 수 있다.

만약 innerFunc 함수가 outerFunc 함수의 내부에서 정의된 중첩 함수가 아니라면 innerFunc 함수를 outer 함수에 내부에서 호출한다 하더라도 outerFunc 함수의 변수에 접근할 수 없다.

이 같은 현상이 발생하는 이유는 자바스크립트가 렉시컬 스코프를 따르는 프로그래밍 언어이기 때문이다.

24.1 렉시컬 스코프

자바스크립트 엔진은 함수를 어디서 호출했는지가 아니라 함수를 어디에 정의했는지에 따라 상위 스코프를 결정한다.

const x = 1;

function foo() {
	const x = 10;
	bar();
}

function bar() {
	console.log(x);
}

foo(); // 
bar(); //

상위 스코프 - 함수를 어디에 정의했느냐에 따라 결정된다. 즉, 상위 스코프는 함수를 정의한 위치에 대해 정적으로 결정되고 변하지 않는다. foo, bar의 상위 스코프는 전역이기 때문에 결과값은 둘 다 1이 나온다.

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

함수가 정의된 환경(위치) 과 호출되는 환경(위치)은 다를수 있다.

렉시컬 스코프가 가능하려면 함수는 자신이 호출되는 환경과는 상관없이 자신이 정의된 환경, 즉 상위 스코프를 기억해야 한다.

이를 위해 함수 자신의 내부 슬롯 [[Environment]]에 자신이 정의된 환경, 즉 상위 스코프의 참조를 저장한다.

24.4 클로저의 활용

클로저는 상태를 안전하게 변경하고 유지하기 위해 사용한다. 즉, 상태가 의도치 않게 변경되지 않도록 상태를 안전하게 은닉하고 특정 함수에게만 상태 변경을 허용하기 위해 사용한다.

const counter = (function () {
// 카운트 상태 변수 
	let num = 0;
	
	// 클로저인 메서드를 갖는 객체를 반환
	// 객체 리터럴은 스코프를 만들지 x
  // 따라서 아래 메서드들의 상위 스코프는 즉시 실행 함수의 렉시컬 환경이다

	return {
	// num: 0, // 프로퍼티는 public 하므로 은닉되지 x 
	
	increase() {
		return ++num;
	},
	decrease() {
		return num > 0 ? --num : 0;
	}
};

}());

즉시 실행 함수가 호출되면 즉시 실행 함수가 반환한 객체 리터럴은 즉시 실행 함수의 실행단계에서 평가되어 객체가 된다. 객체 리터럴은 스코프를 만들지 않고 increase, decrease 메서드의 상위 스코프는 increase, decrease 메서드가 평가되는 시점에 실행중인 실행 컨텍스트인 즉시 실행함수 실행 컨텍스트의 렉시컬 환경이다.

increase, decrease가 어디서 호출되든 상관없이 즉시 실행함수의 스코프 식별자를 참소할 수 있다.

이처럼 클로저는 상태가 의도치 않게 변경되지 않도록 안전하게 은닉하고 특정 함수에게만 상태 변경을 허용하여 상태를 안전하게 변경하고 유지하기 위해 사용한다.

profile
Frontend Developer

0개의 댓글