Index
② 클로저의 활용
A closure is the combination of a function and the lexical environment within which that function was declared.
Mozilla Developer Network. (n.d.). Closures. Retrieved December 27, 2024
1. 클로저와 렉시컬 환경
const x = 1;
// ①
function outer() {
const x = 10;
const inner = function () {
console.log(x);
}; // ②
return inner;
}
// outer 함수를 호출하면 중첩 함수 inner를 반환한다.
// 그리고 outer 함수의 실행 컨텍스트는 실행 컨텍스트 스택에서 팝되어 제거된다.
const innerFunc = outer();
innerFunc(); // 10
🧐 Q. Outer
함수의 지역변수 x
의 값인 10이다. 이미 생명 주기가 종료되어 실행 컨텍스트 스택에서 제거된 outer
함수의 지역 변수 x
가 다시 부활이라도 한 듯 동작하고 있다. 도대체 이는 어떻게 된건인걸까?
Answer) outer
함수의 실행 컨텍스트는 실행 컨텍스트 스택에서 제거되지만 outer
함수의 렉시컬 환경까지 소멸하지는 않는다.
outer
함수의 렉시컬 환경은 inner
함수의 [[Environment]] 내부 슬롯에 의해 참조되고 있고,
inner
함수는 전역 변수 innerfunc
에 의해 참조되고 있으므로 가비지컬렉션의 대상이 되지 않는다.

Nested functions and lexical environments
Ungmo Lee. (2020). Modern Javascript DeepDive. wikibooks. p.396.
2. 클로저의 활용
🧐 Q. 근본적으로 클로저를 왜 사용할까?
Answer)
첫째은 상태 유지(state preservation)를 위해서
=> “변수의 값이 특정 함수가 호출되기 전까지 변경되지 않고 유지하기 위해서”
둘째은 데이터 보호(data encapsulation) 및 제어(controlled access)를 위해서
=> “오로지 특정 함수만 해당 변수의 값을 변경하기 위해서”
const increase = (function () {
let num = 0;
return function () {
return ++num;
};
})();
console.log(increase()); // 1
console.log(increase()); // 2
console.log(increase()); // 3
🧐 Q. 즉시실행 함수 내 선언된 변수와 생성자 함수가 생성할 인스턴스의 프로퍼티간 차이는 무엇일까?
// (1) 즉시실행함수 내 선언된 변수 (private)
const Counter = (function () {
let num = 0; // 클로저 변수
function Counter() {}
Counter.prototype.increase = function () {
return ++num;
};
Counter.prototype.decrease = function () {
return num > 0 ? --num : 0;
};
return Counter;
}());
const counter = new Counter();
console.log(counter.increase()); // 1
console.log(counter.num); // undefined
conuter.num = 10;
console.log(counter.num); // 10
⭐️ 헷갈렸던 부분
// 이는 counter객체에 num 프로퍼티 ≠ 클로저 변수 num과는 관계가 없다.
// 클로저 변수 num과 인스턴스 프로퍼티 num은 서로 다른 렉시컬 환경(Lexical Environment)에 존재하며, 완전히 별개의 개체로 동작한다.
// (2) 생성자 함수가 생성할 인스턴스의 프로퍼티 (public)
const Counter = (function() {
function Counter() {
this.num = 0; // 인스턴스 프로퍼티로 정의
}
Counter.prototype.increase = function() {
return ++this.num; // this.num 참조
};
Counter.prototype.decrease = function() {
return this.num > 0 ? --this.num : 0; // this.num 참조
};
return Counter;
}());
const counter = new Counter();
console.log(counter.increase()); // 1
console.log(counter.decrease()); // 0
console.log(counter.num); // 0 (외부에서 접근 가능)
🧐 Q. 렉시컬 환경을 공유하는 클로저를 만들 수 없을까?
const counter = (function() {
let counter = 0;
return function (predicate) {
counter = predicate(counter);
return counter;
}
}());
function increase(n) {
return ++n;
}
function decrease(n) {
return --n;
}
console.log(counter(increase)); // 1
console.log(counter(increase)); // 2
console.log(counter(decrease)); // 1
console.log(counter(decrease)); // 0
/*
즉시 실행 함수로 호출하면 렉시컬 환경을 공유하는 클로저가 생기고
여기에 증가, 감소 함수를 인자로 넣음으로써 해당 클로저를 공유할 수 있다.
/*
3. 혼동하기 쉬운 케이스
(1)
var funcs = [];
for (var i = 0; i < 3; i++) {
funcs[i] = function() { return i; };
}
// i가 증가된 상태로 끝나고,
for (var j = 0; j < funcs.length; j++) {
console.log(funcs[j]()); // 3, 3, 3
}
(2)
var funcs = [];
for (var i = 0; i < 3; i++) {
funcs[i] = (function(i) {
return function() {
return i;
};
}(i));
}
for (var j = 0; j < funcs.length; j++) {
console.log(funcs[j]()) // 0, 1, 2
}
// 즉시실행함수가 반환한 함수는 클로저이다.자신이 언된 환경(스코프)에 있는 변수에 접근할 수 있는 함수를 말한다.
// 즉, 함수가 외부 함수의 변수에 접근하거나 참조를 유지할 수 있으면 클로저라고 할 수 있다
// 즉시 실행 함수 내부의 i 값은 외부에서 접근할 수 없지만, 반환된 함수는 이 값을 계속 참조합니다.
(1-2) keyword 변화 (var → let)
let funcs = [];
for (let i = 0; i < 3; i++) {
funcs[i] = function() { return i; };
}
// i가 증가된 상태로 끝나고,
for (let j = 0; j < funcs.length; j++) {
console.log(funcs[j]()); // 0, 1, 2
}
// 1. for 루프의 매 반복마다 새로운 블록 스코프가 생성된다.
// 2. let i는 해당 블록 스코프에서 새로 선언되므로, 반복마다 다른 i 변수가 존재한다.
// 3. funcs[i]에 저장된 함수는 각각의 반복에서 생성된 고유의 i를 참조한다.
// 4. 따라서 각 함수는 자신이 생성된 반복에서의 i값을 클로저로서 기억하고 반환한다.

Blocklevel Scope Closure
Ungmo Lee. (2020). Modern Javascript DeepDive. wikibooks. p.415.