??? : "클로저는 함수와 함수가 선언된 어휘적 환경의 조합이다. "
도대체 이게 무슨 말인지 이해가 가는가?
어휘적 환경을 렉시컬 환경이라고 해석해도 환경의 조합이라는 말이 이해가 잘 가지 않는다.
많은 강의와 도서에서 클로저를 단순히 상위 함수의 변수를 기억하는 것으로 설명한다.
혹은 이해를 못할까봐 설명에 도약을 두어 더 혼란스럽게 만들기도 한다.
하지만 클로저의 원리를 완전히 파헤쳐 보면, 왜 그렇게 동작하는지 추가적인 이론과 규칙 없이도 설명이 가능하다.
함수는 실행되면 콜 스택에 올라가며, 콜 스택에 올라간 함수 컨텍스트는 자신이 가지고 있는 정보에 대해 초기화를 한다.
컨텍스트는 Variable Environment
, Lexical Environment
등을 생성한다.
자신이 가진 변수와 함수(메서드)객체를 생성하여 그것들의 주소를 Lexical Environment
안의 Environment Record
에 기록한다.
함수(메서드)객체 내부에는 [[Environment]]
슬롯을 생성하여, 자신의 Lexical Environment
주소를 기록해준다. (선언된 스코프의 주소를 기록)
Variable Environment
, Lexical Environment
에 있는 OuterEEnvironment Reference
에는 자신이 "선언"된 부모의 Lexical Environment
의 주소를 적는다. 이 주소는 본인의 [[Environment]]
슬롯에 기입된 (선언적)부모 함수의 Lexical Environment
주소를 복사하여 사용한다.
4번은 자신의
Lexical Environment
를 메서드의[[Environment]]
슬롯에 기록하는 것이고,
5번은 본인의[[Environment]]
을 확인해서 자신의 부모 주소를 알아오는 것이다.
헷갈리지 말자.
Environment Record
에 없다면, OuterEnvironment Reference
에 기록된 자신의 부모 함수의 Lexical Environment
를 참조하여 사용한다.어떤 함수 Outer가 실행된다. 내부에는 inner함수가 정의되어 있다.
콜 스택에 올라간 Outer는 초기화를 한다. Variable Environment
, Lexical Environment
를 생성한다.
자신이 가진 변수와 메서드를 가리킬 메모리를 생성하고, 그 메모리들의 주소를Lexical Environment
의 Environment Record
에 기록한다.
Lexical Environment
의 OuterEnvironment Reference
주소를 기록하려고 한다. Outer함수 객체 자신이 생성될 때 기록된 [[Environment]]
를 보니, 자신의 부모스코프는 전역스코프이다. 이 주소를 기록한다.
Outer의 컨텍스트 설정을 마치고 이어서 실행되니, inner 함수를 반환하고 종료되는 로직이다.
Outer는 컨텍스트는 종료되었고, Inner 함수가 반환되었다. 이걸 어떤 변수에 잠깐 저장하자.
받아 둔 Inner 함수를 실행시키자 콜 스택에 컨텍스트로 올라갔다.
Inner 컨텍스트도 자신의 렉시컬 환경을 설정한다. 자신의 변수와 메서드가 들어갈 메모리를 할당하고, 자신의 Environment Record
에 기록해 둔다.
OuterEnvironment Reference
를 기록하려고 하니, 앗? 내가 누구로부터 선언되었는지 모르겠다. 자신이 실행되기전에 자신의 부모 컨텍스트 Outer는 없어져 버렸다!
다행히 자신의 족보인 [[Environment]]
를 보니, 부모 컨텍스트의 환경이 기록된 주소가 남아 있다. 이걸 가져다가 자신의 OuterEnvironment Reference
에 적는다.
부모 컨텍스트가 가졌던 환경이 기록된 주소, 즉 Outer의 Lexical Environment
가 소실되지 않고 남은 이유는, 원리 4번과 같이 미리 부모의 렉시컬 환경을 적어두어 참조하는 객체(Inner의 [[Environment]]
)가 있었기에, GC로부터 제거되지 않았던 것이다.
(이렇게 클로저가 발생힌다)
Inner는 실행되면서 자신에게 없는 변수들을 OuterEnvironment Reference
타고 조상의 Lexical Environment
를 참조할 수 있었다.
실행을 마치고 Inner 컨텍스트도 종료되었다.
Inner의 [[Environment]]
와 OuterEnvironment Reference
로 부터 참조를 받던 Outer의 Lexical Environment
는, Inner 컨텍스트가 종료됨에 따라 자신을 찾는 객체가 전부 없어져 버렸다. 참조카운트가 0이므로, GC에 의해 메모리는 회수된다.
(이렇게 클로저는 종료된다)
즉, 클로저는 OuterEnviroment와 [[Enviroment]] 가 Environment Record 의 주소를 참조하므로, Environment Record 의 참조카운트가 0 이 아니기 때문에 발생한다.
정말 기본적이고 근본적인 원리에 의해 클로저 현상이 생기는 것이다.
"클로저는 함수와 함수가 선언된 어휘적 환경의 조합이다" 라는 말은,
함수 본인의 렉시컬 환경 + 함수의 [[Environment]]
와 OuterEnvironment Reference
의 참조로 인해
GC로 부터 '제거되지 않은 상위함수(=함수가 선언된 곳)의 Lexical Environment
'를 모두 사용이 가능하다는 것을 의미한다.