사전적 의미는 폐쇄
,닫힘
,완결성
.
함수형 프로그래밍 언어에서 나타나는 보편적인 현상.
A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function's scope from an inner function. In JavaScript, closures are created every time a function is created, at function creation time.
클로저는 주변 상태(the lexical environment
)에 대한 참조와 함께 묶인(포함된) 함수의 조합입니다. 즉, 클로저는 내부함수로부터 외부함수 scope에 대한 제어(?)를 제공한다. JavaScript에서 클로저는 함수가 생성될 때마다 생성됩니다.
예제소스
var outer = function(){
var a = 1;
var inner = function(){
return ++a;
}
return inner;
}
var outer2 = outer();
console.log(outer2()); //2
console.log(outer2()); //3
outer2
변수는 inner
함수를 호출한 결과값을 가지는것이 아닌, inner
함수 자체를 가짐.(= outer2
함수를 호출할때마다 inner
함수의 실행컨텍스트를 참조.)outer2
변수를 호출할때마다, inner
함수의 실행컨텍스트를 참조하는데, environmentRecord
에는 a
식별자가 없기때문에 outerEnvironmentReference
(=outer
함수의 lexical environment
)를 참조.inner
함수의 outerEnvironmentReference
에 a
변수가 있으니, a
값을 1 증가시킨 결과를 리턴.결과에서 보듯
outer
실행 컨텍스트가 종료가 되었지만,outer2
함수를 호출 할때마다, 변수a
는 전역컨텍스트가 종료될때까지 끝까지 살아 남아있음.
가비지 콜렉터(GC)는 값을 참조하는 변수가 하나라도 있다면, 수집 대상에 포함되지 않음.
(garbage collector란? 프로그램이 동적으로 할당했던 메모리 영역 중에서 필요없게 된 영역을 해제하는 기능)
outer2
함수를 호출할때마다 inner
실행컨텐스트가 활성화 되면서 a
변수를 참조하고 있기 때문에 outerEnvironmentReference
(=outer
함수의 lexical Environment
)를 필요로 하는 상황이라, GC수집 대상에서 제외.클로저란 함수 A에서 선언한 변수 a를 참조하는 내부함수 B를 외부로 전달할경우 A의 실행컨텍스트가 종료된 이후에도 변수 a가 사라지지않는 현상.
(function(){
var a = 10;
var intervalID = null;
var callback = function(){
if(++a >= 20){
clearInterval(intervalID);
}
console.log(a);
}
intervalID = setInterval(callback,1000);
})()
setInterval
의 콜백함수 내부에서 callback
함수의 지역변수에 a
변수와 intervalID
가 없기 때문에 outerEnvironmentReference
가 즉시 실행함수를 참조하는 현상이 발생하기 때문에 클로저가 발생.클로저가 발생한 함수를 소모하지않게 변수를 null or undefined로 할당.
const fruits = ['apple','banana','grape'];
const $ul = document.createElement('ul');
const clickEventHandler = function(fruit){
return function(){
alert(`My Choose is ${fruit}`);
}
};
fruits.forEach(fruit=>{
const $li = document.createElement('li');
$li.innerText = fruit;
$li.addEventListener('click',clickEventHandler(fruit));
$ul.appendChild($li);
})
document.body.appendChild($ul);
fruits
배열 만큼 li
태그를 생성한다음 click
이벤트를 추가했다. click
이벤트의 콜백 함수인 clickEventHandler
는 fruit
값을 인자로 전달하고 익명함수를 리턴한다. 이벤트가 호출이 될 때 반환된 함수안에는 fruit
지역변수가 없기때문에,outerEnvironmentReference
가 clickEventHandler
의 lexical Environment
를 참조하여 해당값을 읽어서 실행결과를 alert
로 띄워준다. 이 때 clickEventHandler
의 익명함수에는 클로저가 존재.
public
,private
같은 접근 제한이 따로 없음. 하지만 클로저를 이용해 흉내내는것은 가능.function counter(){
let privateCounter = 0;
function changeBy(val) {
privateCounter += val;
}
return {
increment: function() {
changeBy(1);
},
decrement: function() {
changeBy(-1);
},
value: function() {
return privateCounter;
}
}
}
const _counter = counter();
console.log(_counter.increment()); // 1
console.log(_counter.increment()); // 2
console.log(_counter.decrement()); // 1
console.log(_counter.value()); // 1
counter
함수는 해당 값을 계산해주는 객체를 반환하는데,객체 안에 있는 메서드를 호출할때마다, 각 메서드에는 각각 내부함수와 지역변수가 없기때문에, outerEnvironmentReference
가 counter
함수의 lexicalEnvironment
를 참고하여, 해당값과 함수를 읽어서 실행하기 때문에 클로저가 발생. 이 때 changeBy
,privateCounter
는 바로 접근해서 사용할수 없기때문에 private
접근자 처럼 된다.
부분 적용함수는 n개의 인자를 받는 함수에 미리 m개의 인자만 미리 넘겨 기억 시켰다가 (n-m)개의 인자를 넘기면 비로소 원래 함수의 실행 결과를 얻을 수 있게끔 하는 함수.
const debounce = function (eventName, func, wait) {
var timerId = null;
return function (event) {
var self = this;
console.log(`${eventName} Trigger!!`);
clearTimeout(timerId);
timerId = setTimeout(func.bind(self, event), wait);
};
};
const keyupHandler = ({target}) => {
console.log(`keyup Event Value is ${target.value}`);
};
const $input = document.createElement('input');
$input.addEventListener("keyup", debounce("keyup", keyupHandler, 1000));
1 . debounce
함수는 eventName
과 실행할 func
와 이벤트 대기시간(wait
)를 받음. 내부 함수에서는 eventName
을 로그에서 출력하고 기존 타이머를 취소하고 setTimeout
가 생성되어 wait
이후에 func
함수 호출.
2 .input
태그 생성하고 keyup
이벤트를 부여. 이벤트 콜백함수는 debounce
함수가 리턴하는 내부함수가 된다.
3 .keyup
이벤트 발생시, 내부함수에서는 eventName
,func
,wait
,timerId
는 내부변수에 없기 때문에 outerEnvironmentReference
가 debounce
함수의 lexicalEnvironment
를 참고한다.
✅ 따라서, eventName
,func
,wait
,timerId
는 클로저로 처리되는 변수.
⚙️실무에서 대표적으로 사용되는 debounce
기법이 대표적인 예.
debounce는 짧은 시간동안 동일한 이벤트가 발생시, 발생한 이벤트를 전부 처리하는것이 아닌 처음 또는 마지막 이벤트에 대해 한번만 처리 하는 기법.
예를 들어, 내용을 입력할때마다, 호출 건수에 제한이 있는 API를 호출할때 마지막에 입력된 단어만을 가지고 API를 호출하면 되기때문에 유용.
커링함수란 여러 개의 인자를 받는 함수를 하나의 인자만 받는 함수로 나눠서 순차적으로 호출될 수 있게 체인 형태로 구성 한 것. 마지막 인자가 전달되기 전까지는 원본 함수가 실행 되지않는것이 특징.
const curryingFunc = function(func){
return function (param1){
return function (param1_1){
return func(param1,param1_1);
}
}
}
//ES6
// const curryingFunc = func=>param1=>param1_1=>func(param1,param1_1);
const customMax = curryingFunc(Math.max)(10);
console.log(customMax(100)); // 100
console.log(customMax(50)); // 50
const customMin = curryingFunc(Math.min)(10);
console.log(customMin(8)); // 8
console.log(customMin(25)); // 10
lazy execution
, 원하는 시점까지 지연시켰다가 필요한 상황에 실행)Redux
의 미들웨어. 공통적으로 store
,next
,action
을 순서대로 받으며, store
는 변하지않는 속성, next
는 dispatch
와 같은의미를 가지며 변하지않는 속성인건 똑같지만, action
는 매번 달라지기 때문에, logger
에 store
,next
는 미리 넘겨서 함수로 저장시킨다음, action
만 받아서 처리할수가 있음. //redux middleware logger
const logger = store=>next=>action=>{
console.log(`dispatch ${action}`);
console.log(`next state ${store.getState()}`);
return next(action);
}