
클로저는 자바스크립트에서 난해하기로 유명한 개념들 중 하나이다. 이전에 학습했던 실행 컨텍스트에 대한 지식과 함께 클로저에 대해 정리해보자.
클로저는 함수와 그 함수가 선언된 렉시컬 환경과의 조합이다.
function outerFunction() {
let outerVariable = 'I am outside!';
function innerFunction() {
console.log(outerVariable); // outerVariable에 접근할 수 있음
}
return innerFunction;
}
const myClosure = outerFunction();
myClosure(); // 'I am outside!'
위 코드에서, innerFunction은 outerVariable을 사용할 수 있는데, 이는 innerFunction이 outerVariable이 선언된 스코프를 기억하고 있기 때문이다. 즉, outerFunction이 실행을 마치고 끝났더라도, innerFunction이 반환되어 나가면 여전히 outerVariable에 접근할 수 있다.
이처럼 외부 함수보다 중첩 함수가 더 오래 유지되는 경우 중첩 함수는 이미 생명 주기가 종료한 외부 함수의 변수를 참조할 수 있다. 이러한 중첩 함수를 클로저(Closure)라고 부른다.
즉, 클로저는 내부 함수가 외부 함수의 변수에 접근할 수 있게 해주는 메커니즘이다. 외부 함수는 실행이 끝났지만 내부 함수는 여전히 외부 함수의 변수를 기억하고 사용할 수 있다.
클로저는 중첩 함수가 상위 스코프의 식별자를 참조하고 있고 중첩 함수가 외부 함수보다 더 오래 유지되는 경우에 한정하는 것이 일반적이다.
클로저는 상태가 의도치 않게 변경되지 않도록 안전하게 은닉하고 특정 함수에게만 상태 변경을 허용하여 상태를 안전하게 변경하고 유지하기 위해 사용한다.
클로저는 상태를 안전하게 변경하고 유지하기 위해 사용한다.
function createAdder(x) {
return function (y) {
return x + y;
};
}
const addFive = createAdder(5);
console.log(addFive(3)); // 8
console.log(addFive(10)); // 15
createAdder(5)는 내부의 x 값을 5로 저장한 클로저를 반환한다. 이 클로저는 x를 기억하고 있기 때문에 addFive는 나중에 y를 받아도 x와의 덧셈 연산을 수행할 수 있다.
상태를 안전하게 은닉하고 특정 함수에게만 상태 변경을 허용하기 위해 사용한다.
function createPrivateCounter() {
let count = 0;
return function () {
return ++count;
};
}
const counter = createPrivateCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
count 변수는 createPrivateCounter 외부에서는 접근할 수 없고, counter 함수에서만 접근이 가능하다.
function delayedLogger() {
for (let i = 1; i <= 3; i++) {
setTimeout(function () {
console.log(i); // 클로저로 인해 각기 다른 `i` 값을 기억함
}, i * 1000);
}
}
delayedLogger(); // 1초 뒤 1, 2초 뒤 2, 3초 뒤 3 출력
setTimeout 함수의 콜백함수는 각기 다른 i값을 기억한다. 만약 var 키워드를 사용했다면 모두 4로 출력되었겠지만, let 키워드를 사용함으로써 블록 스코프가 적용되고 클로저가 정확히 작동하게 된다.