<aside> ❓
클로저는 함수와 그 함수가 선언된 렉시컬 환경과의 조합이다.
</aside>
const x = 1;
function outerFunc() {
const x = 10;
function innerFunc() { // outerFunc의 x 변수에 접근 가능
console.log(x); // 10
}
innerFunc();
}
outerFunc();
const x = 1;
function outerFunc() {
const x = 10;
innerFunc();
}
function innerFunc() {
console.log(x); // 1
}
outerFunc();
const x = 1;
function foo() {
const x = 10;
bar();
}
function bar() {
console.log(x);
}
foo(); // 1
bar(); // 1
그래서 렉시컬 스코프가 가능하려면 호출되는 환경과는 상관없이 자신이 정의된 환경, 즉 상위 스코프를 기억해야 한다.
const x = 1;
function foo() {
const x = 10;
// 상위 스코프는 함수 정의 환경(위치)에 따라 결정된다.
// 함수 호출 위치와 상위 스코프는 아무런 관계가 없다.
bar();
}
// 함수 bar는 자신의 상위스코프, 즉 전역 렉시컬 환경을 [[Environment]]에 저장하여 기억한다.
function bar() {
console.log(x);
}
foo(); // 1
bar(); // 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
자바스크립트의 모든 함수는 자신의 상위 스코프를 기억한다고 했다. 모든 함수가 기억하는 상위 스코프는 함수를 어디서 호출하든 상관없이 유지된다.
자바스크립트의 모든 함수는 상위 스코프를 기억하므로 이론적으로 모든 함수는 클로저다.
하지만 일반적으로 모든 함수를 클로저라고 하지는 않는다.
상위 스코프의 어떤 식별자도 참조하지 않는 함수는 클로저가 아니다.
클로저가 아닌 예시 1
function foo() {
const x = 1;
const y = 2;
// 일반적으로 클로저라고 하지 않는다.
function bar() {
const z = 3;
// 상위 스코프의 식별자를 참조하지 않는다.
console.log(z);
}
return bar();
}
const bar = foo();
bar();
클로저가 아닌 예시 2
function foo() {
const x = 1;
// bar 함수는 클로저였지만 곧바로 소멸한다.
// 이러한 함수는 일반적으로 클로저라고 하지 않는다.
function bar() {
// 상위 스코프의 식별자를 참조한다.
console.log(x);
}
bar();
}
foo();
클로저 예시
function foo() {
const x = 1;
const y = 2;
// 클로저
// 중첩 함수 bar는 외부 함수보다 더 오래 유지되며 상위 스코프의 식별자를 참조한다.
function bar() {
console.log(x);
}
return bar();
}
const bar = foo();
bar();
클로저에 의해 참조되는 상위 스코프의 변수를 자유 변수라고 부른다.
클로저는 상태를 안전하게 변경하고 유지하기 위해 사용한다.
좋지 않은 코드 예시
// 카운트 상태 변수
let num = 0;
// 카운트 상태 변경 함수
const increase = function () {
// 카운트 상태를 1만큼 증가시킨다.
return ++num;
}
console.log(increase()); // 1
console.log(increase()); // 2
console.log(increase()); // 3
위 코드의 개선
// 카운트 상태 변경 함수
const increase = function () {
// 카운트 상태 변수
let count = 0;
// 카운트 상태를 1만큼 증가시킨다.
return ++num;
}
// 이전 상태가 유지되지 못한다.
console.log(increase()); // 1
console.log(increase()); // 1
console.log(increase()); // 1