스코프에 대한 지식이 없다면 먼저 간단히 읽고 오세요.

실행컨텍스트에 대해서 알고 진행해야하기에 실행 컨텍스트 글을 꼭 참고해주세요.

 

실행 컨텍스트(Execution context)

실행 컨텍스트는 실행할 코드에 제공할 환경 정보들을 모아놓은 객체입니다. 자바스크립트는 어떤 실행 컨텍스트가 활성화되는 시점에 선언된 변수를 위로 끌어올리고(호이스팅, hoisting) 외부

okayoon.tistory.com

 

 

 

클로저(Closure)

여러 함수형 프로그래밍 언어에서 등장하는 보편적인 특성입니다.

클로저는 객체지향과 함수형 프로그래밍에서 매우 중요한 개념입니다.

 

특성<- 이라는 것 보이시죠?

자바스크립트 고유의 개념이 아니라서 많은 사람들이 헷갈려하고(나 또한..) 정의를 요약하기가 힘듭니다.

 

 

"자신을 내포하는 함수의 컨텍스트에 접근할 수 있는 함수"

- 더글라스 크록포드, 자바스크립트 핵심가이드

 

"함수가 특정 스코프에 접근할 수 있도록 의도적으로 그 스코프에서 정의하는것"

- 에단 브라운, 러닝 자바스크립트

 

" 컴퓨터 언어에서 클로저는 일급 객체 함수의 개념을 이용하여 스코프에 묶인 변수를 바인딩하기 위한 일종의 기술이다."  

- 위키백과

 

 

위의 글들만 봤을 때 혼란스러웠습니다...

추가적으로 자바스크립트 코어책을 보고 난 후 관련 지식들을 수정해서 작성해보려고 합니다.

 

 

개념

클로저는 어떤 함수 A가 선언된 당시 이 A 함수가 만들어질때 저장된 환경들과 또 다른 함수 B의 환경 사이에서의 상호관계에 따른 현상입니다.

=> 함수가 선언될 당시의 lexical environment(함수 선언당시 실행 컨텍스트 내의 식별자 정보, 외부환경 정보)의 상호관계에 따른 현상입니다.

 

이해가 어려우니 예시를 보고 다시 이야기해보겠습니다.

 

예시1

외부 함수의 변수를 참조하는 내부함수

(여기서 클로저는 식별자 name을 참조하기 위한 내부함수 inner에서 발생하는 현상입니다.)

var outer = function(){
	var name = 'hans';
    
    var inner = function(){
    	console.log(name);
    };
    
    inner();
};

outer();

1) inner 함수 내부에는 식별자 name이 없습니다.

=> 이것은 즉 inner 함수의 lexical environment의 environmentRecord에 name 식별자가 없다는 것을 의미합니다.

=> environmentRecord는 현재 컨텍스트와 관련된 코드의 식별자 정보(매개변수의 이름, 함수 선언, 변수명등)들이 저장해둡니다.

 

2) inner 함수는 lexical environment의 outer-environmentReference에서 상위 함수의 컨텍스트를 찾는데, 이때 상위 컨텍스트는 outer입니다.

=> outer-envirnmentReference는 현재 호출된 함수가 선언될 당시(할성화될 당시)의 LexicalEnvironment를 참조한 것입니다. 

=> 즉, 선언 당시 outer의 lexical environment를 참조합니다.

 

3) inner의 outer-environment로 인해 outer 함수의 lexical environment에서 name 식별자를 찾을 수 있습니다.

=> 스코프 체이닝!

 

4) outer 함수가 끝나게되면 outer함수의 실행 컨텍스트가 종료되며 lexical environment에 저장된 식별자(name, inner)에 대한 참조를 지웁니다.

=> 이때 만약 참조하는 변수가 있다면 메모리에 남아있고, 참조되지 않는다면 가비지 컬렉터의 수집대상이 됩니다.

 

 

예시2

외부 함수의 변수를 참조하는 내부 함수

var outer = function(){
	var a = 1;
    
    var inner = function(){
    	return ++a;
    };
    
    return inner;
};

var outerFunc = outer();
console.log(outerFunc()); // 2
console.log(outerFunc()); // 3

1) inner 함수를 반환하고 있습니다.

2) outerFunc가 선언될 때 outer의 실행 컨텍스트가 종료됩니다.

=> 이때 우리는 식별자 a와 inner가 가비지컬렉팅될 것이라 생각합니다.만 아닙니다.

 

3) outerFunc는 outer의 실행 결과인 inner 함수를 참조합니다.

4) 이후 outerFunc가 호출될 때 앞에서 반환한 inner 함수를 실행합니다.

5) inner가 실행될 때 inner의 lexical environment의 environmentRecord에서 식별자 a를 찾습니다.

=> 없기때문에 lexical environment의 outer-environment에서 상위 함수의 컨텍스트를 찾습니다.

=> inner 함수가 선언된 위치의 lexical environment가 참조복사되기 때문에 outer 함수의 lexical environment를 찾을 수 있습니다. 

 

6) outer의 lexical environment 의 environmentRecord에서 식별자 a를 찾습니다.

 

 

여기서 

"반환된 inner 함수가 실행될때 outer함수 실행 컨텍스트는 종료되었는데, 어떻게 outer함수의 lexical environment에 접근할 수 있는가?"

라는 의문이 들었지만, 코어 자바스크립트 책에서 친절히 설명해줍니다..

 

!!!

이것은 가비지 컬렉터 동작 방식때문이라고 합니다.

가비지 컬렉터는 값을 참조하는 변수가 하나라도 있다면 수집 대상에 포함하지 않습니다.

즉, outer의 컨텍스트가 종료된 뒤에 반환된 inner 함수에서 식별자 a를 참조하고 있고...

outerFunc로 선언되어있기 때문에 언제라도outerFunc가 실행될 수 있음을 인지합니다..

그렇기 때문에 반환된 inner 함수가 활성화 되었을때, 위의 예시대로(lexical environment^^) 동작해야하기 때문에 가비지 컬렉터의 대상이 되지 않으며.... 이것으로 인해 동작이 가능한 것입니다. 

 

그리고 위의 클로저의 예시로 return을 설명했지만

실제로는 외부로 전달이 return만을 의미하지 않으며... return 없이도 클로저가 발생하는 다양한 경우들이 있습니다. 

뭐... 콜백함수를 고차함수로 바꿔 클로저를 활용하는 방식이나 클로저를 통한 변수 보호, 커링함수 등....

=> 커링함수는 여러개의 인자를 받는 함수를 하나의 인자만 받는 함수로 나누어 순차적으로 호출될 수 있게 체인형태로 구성된 함수를 말합니다. 각 호출될 때마다 인자들을 메모리에 저장하고 마지막 호출 시에 가비지컬렉팅 대상이 되는 방식으로 사용하는 방식입니다.

 

 

 

그래서..?

간단히 말하면 클로저는 어떠한 현상에 의해 메모리에 남겨진 변수들의 집합(?)입니다.

=> 어떤 함수에서 선언한 변수를 참조하는 내부함수에서만 발생하는 현상, 외부 함수의 LexicalEnvironment가 가비지 컬렉팅되지 않는 현상을 말합니다.

 

책에서는 더 다양한 예시와 표현들이 있지만,

가장 근접한 표현의 글이라고 작성되어있던것 중 하나의 글을 가져와보았습니다.

 

 

"함수를 선언할 때 만들어지는 유효범위가 사라진 후에도 호출할 수 있는 함수"

- 존레식, 자바스크립트 닌자 비급

 

 

클로저를 이해하고나면 드는 생각은,

메모리를 너무 낭비하는 것 아닌것인가? 라고 생각합니다. 또한 실제로 메모리 누수의 위험을 이유로 클로저를 지양하라고 합니다.

하지만 메모리 소모는 클로저의 본질적 특성입니다.

그리고 이 특성을 잘 활용하는 것이 중요합니다.

 

메모리 관리 중 하나의 예를 들면 null, undefined를 사용하는 것입니다. 

가비지 컬렉터를 이해하여 사용하지 않는 메모리에 대해 회수를 해주는것이 중요합니다.

function init(){
	name = null;
	count = null;
    date = null;
}

(null과 undefined에 대해서도 글한번 읽어보세요.)

 

 

과거에 썼던 글에 오류를 발견하고 수정했는데, 새로쓰게되었습니다..

과거의 글아 안녕.....☆

그때 생각했던 개념과는 많이 달라졌군요...

성장한 것이겠죠.. ? 공부는 이래서 쭉 해야하는 것 같습니다.

 


 

참조

https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Closures

코어자바스크립트 : 핵심개념과 동작원리로 이해하는 자바스크립트 프로그래밍, 정재남

 

+ Recent posts