본문 바로가기
공부/JS

[Javascript] 클로저 알아보기 - 1

by Piva 2024. 1. 5.
  • 실행 컨텍스트에 대해 알아보았던 지난 번에 이어, 자세히 알고 있지 못했던 '클로저'에 대해 공부한 내용을 정리한다.
  • 코어 자바스크립트모던 자바스크립트 Deep dive를 참고했다.
  • 사용 사례 등은 다음 글에서 좀 더 정리하기로.

클로저(Closure)란?

: 함수형 프로그래밍 언어에서 등장하는 보편적인 개념. 즉 자바스크립트의 고유한 개념은 아니다.

  • 때문에 매체마다 다양한 정의로 클로저를 소개하고 있다.
  • MDN에서 클로저의 개념을 발췌하자면 다음과 같다.
"주변 상태(어휘적 환경)에 대한 참조와 함께 묶인(포함된) 함수의 조합입니다. 즉, 클로저는 내부 함수에서 외부 함수의 범위에 대한 접근을 제공합니다. ..."

 

  '어휘적 환경'이란 곧 실행 컨텍스트에서 배웠던 LexicalEnvironment를 의미한다. 정의를 잘 보면 이 어휘적 환경에 대한 참조라는 언급이 익숙함을 느낄 수 있다. 이는 선언된 함수가 자신이 선언될 당시의 LexicalEnvironment를 참조하는 것, 즉 outerEnvironmentReference에 대한 것임을 알 수 있다.

 

  여기서 지난번 outerEnvironmentReference와 이로 인해 발생하는 스코프 체인을 알아볼 때 사용했던 예시 코드를 살짝 변형하여 들고와본다.

var A = function () {
    var foo = 'Hello';
    
    var B = function () {
    	console.log(foo);
    }
    B();
}
A();
  • 위 예시에서, 함수 B 내부의 console문을 살펴보자. console.log를 통해 foo를 출력하고 있는데, B 함수 내부에는 foo가 존재하지 않는다.
  • 따라서 B함수의 실행 컨텍스트 내 outerEnvironmentReference를 통해, B함수가 선언된 당시의 환경(즉, 함수 A의 LexicalEnvironment)을 살펴보게 되고, 여기서 foo를 발견하면 그 값을 출력한다. 따라서 Hello가 출력될 것이다.

  위에서 설명했듯, 함수 B는 자신의 외부 함수에 있는 변수에 접근하고 있다. 이 모습이, MDN에서 제공하는 클로저의 정의, "내부 함수에서 외부 함수의 범위에 대한 접근을 제공합니다"의 표현과 흡사함을 알 수 있다.

 

  이번에는 아래의 예제를 실행했을 때를 살펴본다.

var A = function () {
    var foo = 'Hello';
    
    var B = function () {
    	console.log(foo);
    }

	return B;
}
var A2 = A();
console.log(A2());
  • A 함수를 살펴보면, 내부 함수 B를 선언하고 이 함수 자체를 반환하고 있다.
  • A2는 A 함수의 실행결과, 즉 내부의 B 함수를 참조한다. 따라서 A2 함수를 실행하면, 내부의 B 함수를 실행하는 것과 같다. A 함수는 실행을 마치며 그 실행 컨텍스트가 종료된다.
  • A2 함수를 실행하여 출력해보면, B 함수가 실행된다. B 함수는 내부에 foo가 없으므로, outerEnvironmentReference를 통해 자신이 선언될 때의 환경, 즉 A 함수의 LexicalEnvironment를 참조하여 foo를 발견 후 사용한다.

  위 코드에서는 실행이 이미 종료되어 참조할 수 없는 A 함수의 LexicalEnvironment를 참조하고 있다. 이는 사실 A 함수의 LexicalEnvironment가 가비지 컬렉팅 되지 않았기 때문이다. 가비지 컬렉터는 누군가가 참조하고 있는 대상은 가비지 컬렉팅 대상에 포함시키지 않는다. A 함수는 실행이 종료되었으나 그 LexicalEnvironment를 B에서 여전히 참조하고 있으므로 가비지 컬렉팅 되지 않았고, 여전히 접근 가능한 것이다.

 

  이를 종합하면 다시 클로저를 정의하면 아래와 같이 정리할 수 있다.

  • 외부 함수에서 선언한 변수를 참조하는 내부 함수를 외부로 전달할 경우, 외부 함수의 실행 컨텍스트가 종료된 후에도 변수가 사라지지 않는 현상 (from 코어 자바스크립트)
  • 외부 함수보다 내부 함수가 더 오래 유지되는 경우, 내부 함수가 이미 실행 종료된 외부 함수의 변수를 참조할 수 있는 것. 그러한 상황에서의 내부 함수 (from 모던 자바스크립트 Deep Dive)

 

클로저와 메모리 관리

  • 메모리 소모를 이유로 클로저를 지양해야 한다는 주장도 있으나, 메모리 소모는 사실 클로저에서 일어나는 특성일 뿐이다.
  • 가비지 컬렉팅이 되어야했을 대상을 의도적으로 계속 참조해 컬렉팅이 되지 않도록 하는 것이기 때문이다.
  • 따라서 클로저를 사용하되, 더이상 참조하지 않아도 되는 식별자의 참조 카운트를 0으로 만들어(아무도 참조하지 않도록 하여) 가비지 컬렉팅의 대상이 되도록, 그리하여 메모리 관리가 이루어지도록 해야 한다.
  • 참조 카운트를 0으로 만드는 방법은, 식별자에 기본형 데이터(null 이나 undefined)를 할당하면 된다.

'공부 > JS' 카테고리의 다른 글

[Javascript] 콜백 함수 알아보기  (1) 2024.01.21
[Javascript] 클로저 알아보기 - 2  (1) 2024.01.08
[JavaScript] 실행 컨텍스트에 대하여  (2) 2023.12.23
Typescript 정리 - 2  (1) 2023.01.16
Typescript 정리 - 1  (1) 2023.01.10