모던 자바스크립트 Deep Dive 24장-클로저

HustleKang·2022년 6월 3일
  • 함수를 일급 객체로 취급하는 함수형 프로그래밍 언어에서 사용되는 특성
  • 자바스크립트 고유의 개념이 아니므로 ECMAScript에 클로저는 없음

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

렉시컬 스코프

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

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

함수는 정의된 환경과 호출된 환경이 다를 수 있음
호출되는 위치와 상관없이 자신이 정의된 환경을 알아야 렉시컬 스코프가 가능
이를 위해 함수는 자신의 내부 슬롯 [[Environment]]에 자신이 정의된 환경, 즉 상위 스코프의 참조를 저장함
[[Environment]]에는 현재 실행 중인 실행 컨텍스트의 렉시컬 환경의 참조가 저장됨

함수 객체의 내부 슬롯 [[Environment]]에 저장된 현재 실행 중인 실행 컨텍스트의 렉시컬 환경의 참조가 상위 스코프이고,
또한 해당 함수가 호출되었을 때 생성될 함수 렉시컬 환경의 '외부 렉시컬 환경에 대한 참조'에 저장될 값이기도 하다
함수는 내부 슬롯 [[Environment]]에 저장한 렉시컬 환경의 참조(상위 스코프)를 자신이 존재하는 한 기억한다

const x = 1;

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

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

foo();
bar();

foo 함수를 호출하여 foo 함수 내부에서 bar 함수가 호출되었을 때 실행 컨텍스트는

클로저와 렉시컬 환경

const x = 1;

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

const innerFunc = outer();
innerFunc(); // 10
  • outer 함수 호출 시 inner반환 후 생명주기를 마감, 즉 콜 스택에서 outer 함수의 실행 컨텍스트는 pop된다
  • outer 함수의 지역변수 x는 outer 함수의 실행컨텍스트에 저장되어 있으므로 지역변수 x의 생명주기 역시 마감
  • outer 함수의 지역변수 x에 접근이 불가능해 보이지만 innerFunc에서는 접근 가능

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

  1. outer 함수가 실행되면

  2. outer 함수가 종료되면

    outer 함수의 실행 컨텍스트는 콜 스택에서 제거되지만 outer 함수의 렉시컬 환경은 소멸되지 않음
    outer 함수의 렉시컬 환경은 inner 함수의 [[Environment]] 내부 슬롯에 의해 참조되고 있고, inner 함수는 전역 변수 innerFunc에 의해 참조되고 있으므로 가비지 컬렉션의 대상이 X
    가비지 컬렉터는 참조하고 있는 메모리 공간을 해제하지 않음

  1. innerFunc을 호출하게되면

클로저는 중첩 함수가 상위 스코프의 식별자를 참조하고 있고 중첩 함수가 외부 함수보다 더 오래 유지되는 경우에 한정
중첩 함수가 외부 함수보다 오래 유지되어도 상위 스코프의 식별자를 참조하지 않으면 클로저 X

자유 변수 : 클로저에 의해 참조되는 상위 스코프의 변수
클로저 : 함수가 자유 변수에 대해 닫혀있다 = 자유 변수에 묶여있는 함수

클로저의 활용

상태를 안전하게 변경하고 유지하기 위해 사용
= 상태를 안전하게 은닉하고 특정 함수에게만 상태 변경을 허용

const counter = (function(){
	let num = 0;
    
    return {
    	increase(){
        	return ++num;
        },
        decrease(){
        	return num>0 ? --num : 0;
        }
    };
}());

console.log(counter.increase()); // 1
console.log(counter.increase()); // 2
console.log(counter.decrease()); // 1
console.log(counter.decrease()); // 0

이웅모, 『모던 자바스크립트 Deep Dive』, 위키북스(2021)

profile
grindin'

0개의 댓글