[기술면접/JS] 클로저(Closure)

강민혁·2023년 3월 22일
0

기술면접 | JS

목록 보기
17/17

클로저(Closure)에 대해 설명하세요

Keyword

생명 주기, 중첩 함수(nested function), 상위 스코프, 자유 변수, 렉시컬 스코프, 렉시컬 환경, state, 은닉, 메모리 누수, 모듈 패턴


Script

자바스크립트에서 Closure는 외부 함수보다 더 긴 생명 주기를 가지는 중첩 함수들 중에서, 상위 스코프의 식별자를 참조하고, 이 자유 변수에 대해 묶여있는 함수를 의미합니다. 이것이 가능한 이유는 자바스크립트는 기본적으로 렉시컬 스코프를 따르고, 이에 따라서 중첩 함수는 해당 함수가 선언된 렉시컬 환경을 기억하는데, 외부 함수에서 이 중첩함수를 반환하게 되는 경우에, 이 렉시컬 환경이 소멸되지 않고, 남아있기 때문입니다.

Closure를 활용하면, state를 안전하게 변경하고 은닉할 수 있는 장점이 있습니다. 물론 무분별하게 사용하면, 메모리 누수가 발생할 수 있지만, 자바스크립트에서 모듈 패턴을 구현하는 가장 좋은 방법이라고 알고 있습니다.


Additional

MDN에서의 Closure 정의

"A closure is the combination of a function and the lexical environment within which that function was declared"

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

렉시컬 스코프(lexical scope)

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

렉시컬 환경(lexical environment)과 Closure

실행 컨텍스트 내에, 각 코드 블록에 따른 식별자와 바인딩 된 값들을 담은 lexical environment가 존재한다.

결국 Closure의 정의로 다시 돌아가보면, 함수가 선언되었던 lexical environment를 기억하고 있는 내부 함수와 그 환경을 말하는 것이라 할 수 있다.

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

렉시컬 스코프에서 함수가 자신이 정의된 환경, 즉, 상위 스코프를 기억해야, 자신이 호출되는 환경에 관계없이 작동할 수 있다. 그래서 자바스크립트에서 함수는 자신의 내부 슬롯인 [[Environment]]에 자신이 정의된 환경, 즉, 상위 스코프의 참조를 저장한다.

코드 예시 (JS)

const x = 1;

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

const innerFunc = outer();
innerFunc(); // 10

위 코드를 하나씩 단계 별로 살펴보자.

실행 관점에서 보면,
1. outer 함수 호출
2. innerFunc에 return 값 할당
3. innerFunc 호출

이다.

그럼 호출 과정을 보면,
1. outer 함수 호출
2. x 상수 선언 및 10으로 할당
3. inner 함수 선언
4. inner 함수 return
5. return 된 inner 함수를 innerFunc에 할당
6. innerFunc 호출

그럼 여기서 논점은 'innerFunc의 호출 시점이 아닌, innerFunc의 선언 시점의 렉시컬 환경에 따른 작동'이다.

만약 자바스크립트가 렉시컬 스코프를 따르지 않는다면, innerFunc의 호출 시점에서, 상위 스코프 체인에서의 x 변수를 찾을 것이다. 하지만 작동 결과를 보면 그렇지 않다. inner 함수가 선언된 스코프의 상위 스코프 체인에서의 x 변수를 찾아 출력하고 있다.

그런데 조금 이상한 점이 있다. outer 함수는 한번 호출되고 사라졌다. 그런데, 어떻게 outer 함수 내부에 있던 값을 아직도 저장하고 있는가. 분명 outer 함수는 생명 주기가 종료되었지만, outer 함수의 nested 함수인 inner 함수가 아직 존재하고 있다. 그래서 자바스크립트에서는 이미 생명 주기가 종료한 외부 함수의 변수를 참조할 수 있는 것이다.

Closure가 아닌 경우

상위 스코프 식별자를 참조하지 않는 경우
만약 nested 함수가 상위 스코프의 어떤 식별자도 참조하지 않는 경우에는 일반적으로 closure라고 부르지 않는다. 이 경우는 모던 브라우저에서는 최적화를 통해 상위 스코프를 기억하지 않기 때문이다.

외부로 nested 함수를 반환하지 않는 경우
만약 상위 스코프의 식별자를 참조한다고 하더라도, 외부로 nested 함수를 반환하지 않으면 결국 nested 함수의 생명주기가 외부 함수보다 더 짧아지게 되어, 이 경우는 일반적으로 closure라고 하지 않는다.

Closure의 자유 변수

Closure에 의해 참조되는 상위 스코프의 변수를 자유 변수(free variable)이라고 부른다. 그래서 Closure의 의미는 "함수가 자유 변수에 대해 닫혀있다(closed)."인 것이다. 즉, closure는 자유 변수에 묶여있는 함수라고 이해할 수 있다.


Reference

BOOK - modern javascript deep dive

profile
with programming

0개의 댓글