🤔 클로저가 뭘까?
자바스크립트를 공부하며 몇 번 고비가 왔는데 여러 개의 고비 중 하나인 클로저에 대해 정리하고자 한다.
💡 클로저의 정의
클로저는 영어사전에는 폐쇄, 닫힘 이란 뜻이며
MDN에서는 함수와 그 함수가 선언됐을 때의 어휘적/사전적 환경(Lexical environment)과의 조합 이라고 설명한다.
문장만 보고 나서는 무슨 의미인지 잘 와닿지 않는다.
내가 참고한 코어 자바스크립트에서는
어떤 함수 A에서 선언한 변수a를 참조하는 내부함수B를 외부로 전달할 경우
A의 실행 컨텍스트가 종료된 이후에도 변수a 가 사라지지 않는 현상
라고 정의했다.
엥...? 뭐라는거지 싶었지만 마음을 가다듬고
코드를보면서 느껴보자..
var outer = function (){
var a = 1;
var inner = function (){
console.log(a++)
};
inner();
}
outer();
위 코드가 실행되는 컨텍스트를 보자면
1번 그림과 같이 전역컨텍스트가 쌓이고 => outer 함수 호출에 따라 outer 함수 실행 컨텍스트가 생기고 => 내부 inner 함수 호출에 의해 inner 함수 실행 컨텍스트가 생기고 위에서부터 순서대로 호출이 종료되면 컨텍스트가 사라지게 된다.
이게 특별할 것 없는 코드의 실행 순서이다.
그렇다면 이 코드를 조금만 바꾸어서 클로저의 특별한 현상을 느껴보자
💡 클로저의 특별한 현상
다음 코드를 보자.
var outer = function(){
var a = 1
var inner = function(){
return ++a
}
return inner;
}
var outer2 = outer()
console.log(outer2()) // 2출력
console.log(outer2()) // 3출력
이건 첫번째 코드에서 수정한 코드인데 inner안에서 console을 출력하는 대신 return을 했고,
inner를 호출하는 대신에 return을 했다.
그리고 outer의 실행 결과를 outer2라는 변수에 담았고, 이후에 outer2를 2번 실행한다.
전역 실행 컨텍스트에서는 outer 와 outer2 를 모두 선언은 하지만 outer2는 아직 값이 담기진 않았다.
현재는 값이 undefined 이며 outer 함수의 실행이 종료된 시점에서야 값이 생긴다.
그 다음 outer 실행 컨텍스트가 쌓이는데,
outer 함수 진행에 따라 a에는 1이 들어가고, inner에는 함수가 들어간다.
그 상태에서 실행이 끝나면 이 때 반환된 값 즉 inner가 return 되었으니까 반환된 결과인 inner가 outer2에 담긴다.
그리고 좀 전까지 살아있던 outer 실행 컨텍스트는 지우진 않고 점선 처리를 해놓았다.
이유는 outer2에 의해서 언젠가 inner함수가 호출될 수 있으니까 inner 내부에서 참조하고 있는 outer Environment Recored상의 변수 a는 참조 카운트가 0이 아닌 상태이기 때문이다.
정리하자면
outer 라고 하는 실행 컨텍스트는 종료가 되었지만 내부에서 선언한 변수 a가 사라지지 않고 남아있는 것이다.
원래는 사라져야 하는게 맞는데 참조카운트가 0이 아니라서 살아 남았다. 좀비처럼
변수 a는 좀비가 된 상태로 outer2 함수를 호출해 보면 inner 함수가 실행되기에 inner 함수에대한 실행 컨텍스트가 쌓인다
inner 컨텍스트 내부에서 a에 접근을 했는데 a는 environmentRecord에는 없고, outerEnvironmentReference에 a가 있다.
따라서
outerEnvironmentReference에 있는 a 가 1에서 2로 바뀐다.
그러면 점선처리된 outer 실행 컨텍스트 내부의 좀비a는 참조를 하고 있을 뿐이니까 a가 2로 같이 바뀐다.
그리고 반환하면서 종료가 되면 inner실행 컨텍스트가 제거가 되고, 콘솔이 출력된다. 첫번째 콘솔 값 : 2
다시 outer2를 또 호출하면 inner 컨텍스트가 쌓이면서 a에 2였던게 3이 되고 inner 컨텍스트가 끝나고 3이 출력된 다음에 전역 컨텍스트도 종료되면서 끝이 난다.
하지만 a 변수는 전역 컨텍스트가 종료되기 전까지 사라지지 않는다.
이유는 outer2 변수가 inner함수를 들고 있는데, 다시 inner 함수의 outerEnvironmentRefernce로부터 a를 참조하기 때문이다.
참조 카운트가 0이 되지 않기 때문에 계속 남아있는건데, 이 참조카운트를 0으로 만들려면 어디선가 이 연결고리를 끊으면 된다.
예를 들면 outer2 참조 함수에 다른것을 대입하면 된다.
앞전에 클로저를
어떤 함수 A에서 선언한 변수a를 참조하는 내부함수B를 외부로 전달할 경우
A의 실행 컨텍스트가 종료된 이후에도 변수a 가 사라지지 않는 현상
라고 정의 했는데, 이렇게 변수a 가 사라지지 않고 계속 남아있는 현상이 클로저의 특별한 현상이다.
💡 클로저를 통해 얻을 수 있는 이점
이 특별한 현상에 의해서는 함수 종료 후에도 지역변수를 사라지지 않게 할 수 있다
사용자가 선택적으로 지역변수 중에서 어떤거는 사라지게 하고 어떤거는 사라지지 않게 할 수 있다라는 말이다.
출처:
https://developer.mozilla.org/ko/docs/Web/JavaScript/Closures
코어 자바스크립트
제가 기록한 내용이라 정확하지 않은 정보가 있을 수 있습니다.
'JavaScript' 카테고리의 다른 글
[JavaScript] 브라우저에서 일어나는 일 (feat. Event Loop) (1) | 2022.12.15 |
---|---|
[JavaScript] this에 관한 탐구 (0) | 2022.12.03 |
[JavaScript] 프로토타입 (prototype) (0) | 2022.06.29 |
[JavaScript] defer, async 스크립트 (0) | 2022.05.14 |
[JavaScript] 메서드와 this (0) | 2022.04.27 |