본문 바로가기
개발 관련 강의 정리/10분 테코톡

[10분 테코톡] 꼬재의 클로저 정리

by 코딩개발 2023. 6. 12.
728x90
반응형

 


클로저 현상의 동작방식에 대해 실행 컨텍스트의 LexicalEnvironment, LexicalEnvironmentRecord, OuterEnvironmentReference를 통해 알아보자.

LexicalEnvironmentRecord를 Record로 OuterEnvironmentReference를 Outer로 축약해서

 

위와 같은 형태로 실행 컨텍스트를 표현할 예정이다.

 

 

클로저

MDN에서는 클로저를 함수와 함수가 선언된 어휘적 환경의 조합이다 라고 정의되어 있다. 이해하기 이해하기 어려울 수 있으니 이해하기 편한 코어 자바스크립트에서 해석한 클로저의 정의를 보면, 클로저를 어떤 함수 a에서 선언한 변수 a를 참조 하는 내부함수 B를 외부로 전달할 경우 A의 실행 컨텍스트가 종료된 이후에도 변수 a가 사라지지 않는 현상이라고 말한다.

 


위에 doSomething 함수는 내부에서 x 변수에 10을 할당하고 sum 함수를 리턴하는 함수이다. 이 예제를 통해 앞서 언급한 클로저 정의에 대한 해석을 적용하자면, doSomething 함수에서 선언한 x 변수를 참조하는 내부함수 sum을 외부로 전달할 경우

 


x 변수가 사라지지 않는 현상을 말한다.


아래 예제코드를 보며 클로저 현상의 동작방식을 실행 컨텍스트를 통해 살펴보도록 하자

 

 

위 예제코드는 sum 함수에서 익명함수를 리턴하고 리턴된 익명함수를 add 변수에 할당해 add 변수를 호출한 결과를 출력하는 코드이다. 이 코드를 한 줄씩 확인해 보자

 

자바스크립트에서는 코드를 실행하기에 앞서 코드 평가를 먼저 진행하게 된다.

 

 

첫 번째로 Global에 LexicalEnvironment를 생성하고 전역 코드 평가가 이루어진다.

 

 

코드 평가에서는 sum 함수와 add 변수를 호이스팅해

 


Record에 add와 sum 함수를 기록해둔다. 현재 전역 코드 평가가 이루어지고 있기 때문에 Outer에는 null이 할당되고

 


콜스택에 Global의 LexicalEnvironment를 push하게 된다.

 


코드평가 과정이 종료되고 코드가 순차적으로 실행되는데 add 변수에 값을 할당하기 위해 sum 함수를 호출하게 된다.

 

 

Global의 LexicalEnvironment 코드평가 과정과 동일하게 sum 함수도 sum의 LexicalEnvironment를 생성하고 sum 함수 내부의 코드평가가 이루어진다.

 


코드평가에서는 매개변수 x와 return문에 있는 익명함수를 호이스팅해 Record에 기록해둔다. 다음으로 Outer에는 상위 스코프인 Global의 LexicalEnvironment를 가리키게 하고

 


콜스택에 sum의 LexicalEnvironment를 push하게 된다.

 


사실 여기서 sum의 LexicalEnvironment의 Outer는 함수가 호출되어 평가되는 시점이 아닌 함수가 선언되는 시점에 결정된다. 즉, Global의 LexicalEnvironment 코드 평가과정에 sum 함수가 선언되었으니

 

 

sum의 LexicalEnvironment Outer에는 Global의 LexicalEnvironment를 가리키게 되는 것이다.

 


sum 함수의 코드평가 과정이 종료되었으니 코드가 한 줄씩 실행되는데

 


매개변수 x에는 인자로 넘겨준 2를 할당하고 레코드 안의 x 변수에 2를 기록한다.

 


이후 실행할 코드가 없으니 sum의 LexicalEnvironment는 콜스택에서 pop되고

 


다시 Global의 LexicalEnvironment로 돌아와 add 변수에 sum 함수의 리턴값인 익명함수를 할당하고

 


Record에 있는 add 변수에 익명함수를 할당한다.

 


마지막 전역 코드인 console.log를 실행하기 위해 add 함수를 호출한다.

 

 

add 함수의 호출이 일어나면

 


add에 LexicalEnvironment를 생성하고 add 함수 내부 코드평가가 이뤄지는데

 


매개변수 y를 호이스팅해 레코드에 기록한다. 다음으로 Outer에는 sum의 LexicalEnvironment를 가리키게 한다.

 


현재 콜스택에는 Global의 LexicalEnvironment만 존재한다. 어떻게 add의 LexicalEnvironment의 Outer는 sum의 LexicalEnvironment를 가리키게 할 수 있을까?


이러한 현상을 가지고 있는 자바스크립트의 특성 때문에 클로저라는 현상이 일어나게 되는 것이다.

 


다시 예제코드를 보며 클로저 현상의 동작방식을 살펴보자
sum함수 안의 익명함수의 Outer가 가리키는 LexicalEnvironment의 결정은 익명함수가 선언되는 시점에 결정되기 때문이다. 즉, sum 함수 내부 코드 평가 과정을 진행할 때 현재 실행중인 실행 컨텍스트인 sum의 LexicalEnvironment를 익명함수의 Outer에 먼저 등록을 하는 것이다. 다시 말해, 어떤 함수의 Outer는 어떤 LexicalEnvironment에서 선언되었는지가 중요한 것이다.

 


다시 기존에 보던 코드로 돌아가보면 앞서 이야기한 이유에 의해 add의 LexicalEnvironment의 Outer는 콜스택에 없는 sum의 LexicalEnvironment를 가리킬 수 있으니

 

 

이제 add의 LexicalEnvironment는 콜스택에 push 된다.

 


add 함수 내부 코드의 평가 과정의 끝이 나고 코드를 한 줄씩 실행하게 되는데 매개변수 y에 7을 할당하고

 


Record 안의 y 변수에 7을 기록한다.

 


다음으로 return문을 실행하게 되는데 이 때 add의 LexicalEnvironment Record 안에 존재하지 않는 x 변수를 마주하게 된다. 자바스크립트의 변수 탐색 과정은

 


우선 본인의 Record에 기록되어 있는 변수를 확인하고 해당하는 변수가 없다면

 


Outer가 가리키는 LexicalEnvironment의 Record를 확인하게 된다.

 


이 과정을 반복하여 Outer가 가리키는 LexicalEnvironment의 Record를 탐색하는 건데 최종적으로 Global의 LexicalEnvironment Record에도 변수가 존재하지 않는 경우에 ReferenceError가 발생되는 것이다.

 

앞서 이야기한 변수 탐색 과정을 적용하여 x 변수에 할당된 값을 찾아가는 과정을 통해 클로저 현상의 동작방식에 대해 알아보자

 

 

x 변수는 첫번째로 실행 중인 실행 컨텍스트인 add의 LexicalEnvironment Record를 탐색하게 된다. 하지만 add의 LexicalEnvironment Record 안에 x 변수가 선언되어 있지 않기 때문에

 

 

다음으로 add LexicalEnvironment Outer가 가리키고 있는 sum의 LexicalEnvironment를 확인하게 된다.

 


sum의 LexicalEnvironment Record를 확인하니 x 변수에 2가 할당되어 있는 것을 확인하고 다시 본래 코드로 돌아가게 된다.

 


다시 한 번 정리하자면 sum의 LexicalEnvironment는 콜스텍에 존재하지 않지만 add의 LexicalEnvironment의 Outer가 가리키고 있는 sum의 LexicalEnvironment가 가지고 있는 변수 X 가 변수 x가 사라지지 않는 현상을 클로저 현상이라고 말할 수 있다. 그리고 이처럼 클로저에 의해 참조되는 x 변수는 자유변수라고도 부른다.

 


그럼 다시 저희 예제 코드로 돌아와 x는 2, y는 7이니 9가 리턴이 되고

 


add의 LexicalEnvironment는 콜스택에서 pop 된다.

 


다시 Global의 LexicalEnvironment의 코드가 실행되는데 마지막 남은 console.log를 실행하여 9를 출력하고

 


Global의 LexicalEnvironment도 콜스택에서 pop되며 프로그램은 종료된다.


참고

https://www.youtube.com/watch?v=PJjPVfQO61o&ab_channel=%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC 

728x90
반응형

댓글