클로저를 이해하기 위해 '실행 컨텍스트(EC)'의 개념이 필요합니다.
먼저 '실행 컨텍스트(EC)' 부터 간략히 알아볼까요?
실행 컨텍스트(Execution Context)
실행 가능한 코드가 실행되기 위해 필요한 환경을 말합니다. 즉, 자바스크립트가 실행되는 환경이며 코드가 실행되기 위한 모든 정보가 저장되어 있습니다.
실행 컨텍스트의 종류
크게 전역 실행 컨텍스트와 함수 실행 컨텍스트가 존재합니다.
1. 전역 실행 컨텍스트(Global Execution Context)
- default context
- 단 한 개만 정의되는 컨텍스트입니다.
- Call Stack에 가장 먼저 추가되어 앱이 종료할 때 삭제됩니다.
- 함수 밖의 코드를 위해 존재합니다.
2. 함수 실행 컨텍스트(Function Execution Context)
- 전역 실행 컨텍스트와 달리 각 함수 호출별로 새로운 실행 컨텍스트가 생성됩니다.
- 함수 실행이 종료되면 Call Stack에서 제거됩니다.
실행 컨텍스트의 내부
아래 3가지 요소가 생성됩니다.
- 변수 객체(Variable Object) : let, const, var 변수 선언부, 함수, argument 객체
- 스코프 체인(Scope Chain)
- this : 각 컨텍스트는 this를 갖습니다.
⚠️ 화살표 함수의 실행 컨텍스트는 argument 객체와 this를 갖지 않습니다!
실행 컨텍스트의 생성 과정
const name = 'Tom';
const first = () => {
let a = 1;
const b = second(7, 9);
a = a + b;
return a;
}
function second(x, y) {
var c = 2;
return c;
}
const x = first();
먼저 전역 실행 컨텍스트가 생성되며, 화살표 함수 first를 위한 함수 실행 컨텍스트, 함수 second를 위한 함수 실행 컨텍스트 순으로 생성됩니다.
각 실행 컨텍스트는 다음과 같은 정보를 포함합니다.
전역 실행 컨텍스트 | first() 함수 실행 컨텍스트 | second() 함수 실행 컨텍스트 |
변수 name = 'Tom' 화살표 함수 first = <f> 함수 second = <f> 변수 x = <unknown> |
변수 a = 1 변수 b = <unknown> |
변수 c = 2 argument 객체 = [7, 9] |
Call Stack에 실행 컨텍스트가 쌓이는 순서
Call Stack은 실행 컨텍스트가 쌓이는 곳입니다.
- 먼저 name 변수를 포함한 함수 밖의 코드를 위한 전역 실행 컨텍스트가 생성된다.
- 변수 x의 first 함수 호출에 의해 화살표 함수 first의 함수 실행 컨텍스트가 생성된다.
- 화살표 함수 first 내부의 second 함수 호출에 의해 second의 함수 실행 컨텍스트가 생성된다.
- 각 함수가 종료되면 해당 실행 컨텍스트는 Call Stack에서 제거된다.
후 여기까지 잘 따라오셨나요???
드디어 이제 여러분들은 클로저를 이해할 준비가 되었습니다!!!
클로저
클로저의 정의를 어려워하시는 분들을 위해 다양한 정의들을 준비해보았습니다. ✌️
여러 번 읽은 후 클로저에 대해 감을 잡아보자구요!
- 함수가 선언된 환경의 스코프를 기억하여 함수가 스코프 밖에서 실행될 때에도 기억한 스코프에 접근할 수 있게 만드는 문법을 말한다.
- 클로저는 실행 컨텍스트의 변수 환경(Variable object, 변수 객체)를 가리키며, 이는 해당 실행 컨텍스트가 Call Stack에서 제거되었더라도 변수 환경(Variable object, 변수 객체)를 가리킨다.
- 클로저는 해당 상위 함수가 리턴된 이후에도 상위 함수의 모든 변수에 대해 접근이 가능하다. 또한 외부 범위에 대한 참조를 유지하므로 스코프 체인이 보존되어 상위 스코프에 접근할 수 있다.
- 클로저는 반환된 내부 함수가 자신이 선언되었을 때의 환경(렉시컬 환경)인 스코프를 기억하며 스코프(자신이 선언되었을 때의 환경) 밖에서 호출되어도 언제나 그 환경(스코프)에 접근할 수 있다.
- 클로저는 자신이 생성될 때의 환경(렉시컬 환경)을 기억한다.
- 클로저는 내부 함수가 유효한 상태에서 외부 함수가 종료하여 외부 함수의 실행 컨텍스트가 반환되어도 외부 함수 실행 컨텍스트 내의 활성 객체(Activation object)는 내부 함수에 의해 참조되는 한 유효하여 내부 함수가 스코프 체인을 통해 참조할 수 있는 것을 의미한다.
활성 객체(Activation object) : 함수 실행 컨텍스트의 변수 객체(Variable Object)는 활성 객체(Activation object)를 가리킵니다. 활성 객체와 변수 객체를 다르게 부르지만 변수 객체가 활성 객체를 사용하므로 둘 다 같은 의미라고 생각해도 좋습니다. 😮
클로저의 예시
클로저의 예시를 통해 클로저를 더 이해해볼까요?
function add(num) {
let cnt = 0;
return function () {
cnt++;
console.log(cnt + num);
};
};
const adding = add(10);
adding(); // 11
adding(); // 12
adding(); // 13
console.dir(adding);
console.dir를 통해 adding 데이터를 확인하면 다음과 같습니다.
클로저 내부에 반환된 함수 내부에는 없는,
즉, 반환된 함수 외부의 변수들이 포함되어 있음을 알 수 있습니다.
- add 함수가 반환한 함수 내에서 외부 함수의 변수 객체(VO)인 num과 cnt를 사용하고 있습니다.
- 즉, adding 함수는 add 함수가 리턴문에 의해 종료되어 add 함수의 실행 컨텍스트가 Call Stack에서 제거되었더라도 adding 함수가 사용하는 외부 함수의 변수 객체(VO)에 접근할 수 있습니다.
- 마치 클로저는 외부 함수의 변수 객체(VO)를 수집하는 배낭과도 같다고 비유할 수 있습니다.
위 코드를 통해 클로저의 생성 과정을 알아봅시다.
클로저의 생성 과정
function add(num) {
let cnt = 0;
return function () {
cnt++;
console.log(cnt + num);
};
};
const adding = add(10);
adding(); // 11
adding(); // 12
adding(); // 13
console.dir(adding);
- 전역 실행 컨텍스트와 add 함수 호출에 따른 함수 실행 컨텍스트가 생성되어 차례로 Call Stack에 추가된다.
- add 함수의 실행이 끝나 add 함수의 실행 컨텍스트는 Call Stack에서 제거된다.
- adding 함수의 스코프 상에서는 변수 cnt나 매개변수 num에 접근할 수 없다. adding 함수의 내부 스코프에 해당 변수들이 존재하지 않으며 외부 스코프에서도 찾아볼 수 없기 때문이다.
하지만 모든 함수는 함수가 생성된 실행 컨텍스트의 변수 환경(VE)에 항상 접근할 수 있습니다.
즉, add 함수 내부에서 adding 함수가 만들어졌으므로 adding 함수는 add 함수의 변수 객체(VO)에 접근할 수 있습니다.
😮 변수 객체(VO)는 변수 뿐만 아니라 함수의 argument도 포함되므로 위 예제에서 adding 함수는 add 함수 내부의 변수 cnt와 매개변수 num에도 접근할 수 있다.
참고 자료
'Javascript' 카테고리의 다른 글
SessionStorage를 이용해 캐시 구현하기 (0) | 2022.11.23 |
---|---|
[JS] DocumentFragment, Template tag (0) | 2022.10.26 |
spread와 rest 비교하기 (0) | 2022.07.28 |
프로토타입(Prototype) 이해하기 2 (0) | 2022.07.17 |
프로토타입(Prototype) 이해하기 1 (0) | 2022.07.13 |