클로저를 이해하기 위해선 실행 컨텍스트 개념을 알아야 한다.
📌 클로저
Lexical Environment의 조합. 상위 스코프의 식별자를 참조하는 하위 스코프의 함수가 더 오래 살아 있는 것
함수가 특정 스코프에 접근할 있도록 의도적으로 그 스코프에서 정의하는 것
함수라기 보단 이러한 상황을 클로저라고 의미한다.
특정함수의 EnvRec를 외부에서 참조할 수 있도록 의도적으로 return되는 함수이다.
let userFn;
{
const privateUser = { id: 1, name: 'Hong' };
userFn = () => privateUser; // 이 user 변수가 하위(Block) 스코프의 privateUser를 참조
}
// block은 끝나서 이 block의 BlockExecutionContext는 사라졌지만,
// privateUser를 user가 계속 참조하고 있어 BlockLexicalEnvironment는 사라질 수 없다!!
userFn().age = 30; // user refer to privateUser ⇒ 실제로 privateUser가 변경!
console.log(userFn); // { id: 1, name: 'Hong', age: 30 }
클로저는 다른 비순수 함수를 순수함수로 바꾸는 목적도 있다.
function discount() { // cf. currying
const dcRate = 0.1; // private variable
return function (price) { // 내부함수(:외부에서 dcRate 참조 가능하도록하는 함수를 반환)
return price * dcRate; // dcRate를 외부에서 직접 접근 못하지만 이 함수는 가능
}; // 즉, 외부에서 현재 할인율을 알 수는 없음!
}
const items = [
{ item: '상품 A', price: 32000 }, { item: '상품 B', price: 45000 },
];
const dc = discount();
for (const { item, price: orgPrice } of items) {
const salePrice = orgPrice - dc(orgPrice); // 실제 판매 금액
console.log(`${item}: ${orgPrice}원 --> ${salePrice.toLocaleString()}원`);
}
✅ currying
인자를 여러개 받는 함수를 분리하여 인자를 하나씩만 받는 함수로 만드는 방법이다.
함수형 프로그래밍 기법 중 하나로 함수를 재사용하고 리팩터링 하기 쉽게 하는 방법이다, 특히 커링은 함수를 재사용할 때 되게 유용한 것 같다!
//currying
const MENU = { chinese: ["짜장면", "짬뽕"], itealian: ["피자", "파스타"] };
function restaurant(kind) {
const menu = MENU[kind];
console.log(menu);
return function (menuIndex) {
return menu[menuIndex];
};
}
const lunch = restaurant("chinese"); // chinese로 식당이 한정이 된다.
console.log(lunch(1));
const dinner = restaurant("itealian"); // itealian 식당이 한정이 된다.
console.log(dinner(1));
이와 같이 공통적으로 사용하는 것들을 묶었지만 함수를 또 분리하여 만든다? 라고 생각하면 될 것 같다.
블로그에 있는 예시인데, 이게 가장 왜 사용하고 언제 사용할지 감이 오는 것 같았다.
예를 들어 장바구니에 담긴 물건들의 가격을 계산한다고 가정해보자.
커링을 적용하기 전 multiple_v1 함수로는 아래와 같이 작성이 가능하다. 아래 함수는 부과세라는 똑같은 변수를 shirts_price,pants_price 에 담고 있다. 이는 고정 값임에도 불구하고 중복적으로 매번 전달해 줘야된다.
const VAT = 1.05; // 5% VAT
// a -> 부과세, b -> 가격, c-> 갯수
const shirts_price = multiply_v1(VAT, 20000, 4);
const pants_price = multiply_v1(VAT, 15000, 4);
하지만 커링을 적용해보자.
const VAT = 1.05;
const multiply_VAT = multiply_v2(VAT); // 부가세가 첫번째 인자로 전달된 함수를 multply_VAT 변수에 선언
const shirts_price = multiply_VAT(20000)(4);
const pants_price = multiply_VAT(15000)(2);
부가세가 첫번째 인자로 전달되어 multiply_VAT 이라는 변수에 선언해 주고 나머지 별도 인자들만 전달해 주었다.
이로써 많은 인자가 생길 수록 함수의 분리하여 인자를 일부만 받는 방법인 커링을 사용하면 코드의 재사용성이 높아지게 된다.
⚠️ 그렇지만 주의 할 점이 있다면 커링은 인자의 순서가 매우 중요하다. , 하지만 타입스크립트를 사용하면 괜찮지 않을까 싶음
실전에서 적용하기
커링 기법을 통해 이벤트핸들러를 더욱 깔끔하게 작성할 수 있다.
// 기존의 방식 : 인자가 없을 경우
<button onClick = {handleClick}>전송</button>
// 기존 방식: 인자가 있을 경우, 익명함수로 전달
<button onClick = {() => handleClick(value)}>전송</button>
// 커링 기법 적용, 익명함수를 전달해 줄 필요가 없다.
const handleClick = (value) => () => { console.log(value) };
<button onClick={handleClick(value)}>전송</button>
참고
'JavaScript' 카테고리의 다른 글
[JS] 함수 (바인딩, this, 익명, 즉시 실행 함수) (0) | 2025.04.10 |
---|---|
[JS] Object&Property (0) | 2025.04.10 |
[JS] 메모이제이션 (0) | 2025.04.09 |
[JS] 스코프 실행컨텍스트 (0) | 2025.04.08 |
[JS] Strict Mode (0) | 2025.04.08 |