클로저란?
function returnX() {
return 'x';
}
const x = returnX()
typeof x
function returnX() {
let x = 'x';
return function returnY() {
return x + 'y';
};
}
const x = returnX();
typeof x;
const x = returnX()()
typeof x;
function returnChar(x) {
let outerChar = x;
return function returnChar2(y) {
let innerChar = y;
return outerChar + innerChar;
};
}
const x = returnChar('x');
const xy = x('y');
const xz = x('z');
const xc = x('c');
- 시점을 기억, 바인딩한다는 느낌은 아래의 예시도 마찬가지임
function sum(num1) {
return function(num2) {
return num1 + num2;
};
}
const sum5 = sum(5);
sum5(10);
sum5(20);
sum5(30);
const sum10 = sum(10);
sum10(5);
sum10(15);
function sum(num1) {
return function(num2) {
return function(num3) {
return num1 + num2 + num3;
};
};
}
const sum5 = sum(5);
const sum10 = sum(10);
sum5(10)(10);
sum5(20)(10);
sum5(30)(10);
sum10(5);
sum10(15);
- 클로저는 이런식으로 어휘적인 환경을 기억시켜 놓을 수 있는 함수임
- 위를 화살표 함수로도 줄여 쓸 수 있음
const sum => (num1) => (num2) => (num3) => num1 + num2 + num3;
const sum5 = sum(5);
const sum10 = sum(10);
sum5(10)(10);
sum5(20)(10);
sum5(30)(10);
sum10(5);
sum10(15);
- 결국, 클로저는 이전 환경을 기억해두고, 캡쳐시켜놓을 수 있는 함수 기법임, 클로저를 억지로 쓸 필요는 없지만, 이에 대해서 활용한 케이스를 생각하면 됨
은닉화
- IIFE를 활용해서 은닉화를 한 것을 기억할 것임, IIFE를 통해서 즉시실행함수 개념보다, 이를 일종의 스코프로 쓴다고 생각하면 좋음
- 이는 함수도 마찬가지임, 아래의 예시치럼 함수 내부의 값에 접근을 못하고 바로 실행을 시켜야함
function a() {
let temp = 'a';
return temp;
}
const result = a();
result;
- 이럴 때 아래와 같이 클로저를 활용해서 앞서 은닉화 된 값을 접근하여 볼 수 있는 것
function privateData() {
let temp = 'a';
return {
value: function() {
return temp;
},
};
}
const private = privateData()
private.value()
function privateData() {
let temp = 'a';
return {
value: function() {
return temp;
},
changeValue: function (newVal) {
temp = newVal;
},
};
}
const private = privateData()
private.value()
private.changeValue('b');
private.value()
const private2 = privateData()
private2.value()
function CounterApp(initValue) {
let countValue = initValue ?? 0;
return {
value: function() {
return countValue;
},
increment: function () {
countValue++;
},
decrement: function () {
countValue--;
},
};
}
const counter1 = CounterApp(1);
const counter2 = CounterApp(2);
counter1.value();
counter2.value();
counter1.increment();
counter1.increment();
counter1.increment();
counter1.increment();
counter1.value();
counter2.value();
- 이를 과거에는 모듈 패턴이라고 불렀음, 클로저를 이용해서 각자의 어휘적 환경, private 데이터를 넣어두고 그 데이터를 바꿔주는 함수를 넣어줌(마치 흡사 getter, setter를 쓰듯이)
- this도 없는데 초기값이 다른 것을 각자의 스코프가 유지되고 그 스코프에 따라 계산 및 처리가 됨, 이런식으로 클로저를 썼음
- 함수안에 값을 숨기고 싶을때 클로저를 쓸 수 있다고 알아두면 좋음
활용 사례
- debounce 이벤트, 이벤트 실행시 과하게 실행되는 것을 지연을 하는 기법, 구간 스크롤, 클릭 여러번 시 지연시킬 떄 주로 씀
- 이럴 때 클로저를 많이 씀, 아래와 같이 클릭이 되는 시점의 함수를 기억하고 있다고 이벤트가 실행 되는 시점에 실행을 시키는 것
- 아래와 같을 때 클로저를 가장 많이씀
buttonElement.addEventListener(
'click',
debounce(handleClick, 500),
);
function debounce(func, timeout = 300) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => { func.apply(this, args); },
};
}