클로저란?
함수가 선언될 당시의 스코프(렉시컬 환경)을 기억해서 외부함수의 변수에 접근할 수 있는 함수를 말한다.
클로저의 핵심
- 함수안에 함수가 있는 경우 대부분 클로저라고 한다.
- 외부함수가 종료된 경우에도 내부함수가 외부함수에 접근할 수 있다.
예제
function makeCounter() {
let count = 0;
return function () {
count += 1;
return count;
};
}
const counter = makeCounter(); //makeCounter 함수는 여기서 이미 종료됨
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
// 대신 counter가 그 환경을 기억함
count변수는 makeCounter 내부에 존재하지만 counter 함수가 환경을 기억해서 계속 접근이 가능하다.
클로저는 자신이 선언된 위치의 Lexical scope를 기억하며 이를 [[Environment]]내부 슬롯에 저장해두고 계속 참조가 가능하다.
순수함수와 비순수함수
✔️ 순수함수란?
입력과 출력이 항상 일정하고, side-effect가 없는 함수를 말한다.
✔️ 비순수함수란?
외부상태에 의존하거나, 외부상태를 변경하는 함수를 말한다. side-effect가 발생할 수 있다.
동일한 입출력보장, 외부의 영향이 없기에 디버깅이 쉬움, 함수의 조합이 쉬움 등등의 이유로 우리는 비순수함수 보다는 순수함수를 사용해야 될 필요가 있다.
커링 (Currying)
하나의 함수에 여러인자를 받는 대신, 인자 하나씩을 받아 여러단계에 걸쳐 처리하는 방식을 말한다.
예제
const MENU = { chinese: ["자장면", "탕수육"], italian: ["pizza", "pasta"] };
function restaurant(kind) {
const menu = MENU[kind];
return function (menuIndex) {
return menu[menuIndex]; // menu['chinese']
};
}
//첫번째로 음식의 종류를 먼저받고, 그다음에 메뉴 idx를 받아 처리한다.
//1차로 음식의 종류로 menu판를 리턴받고, 그 안에 idx에 따라 menu를 리턴하는 클로저를 리턴하는 형태
const lunch = restaurant("chinese");
console.log(lunch(1));
const dinner = restaurant("italian");
console.log(dinner(0), dinner(1));
✔️ 커링을 쓰는 이유
- 인자를 한번에 받지않고 분리해서 받기 때문에 유연하게 재활용 등을 할 수 있다.
- 고차 함수와의 궁합이 좋다. (map, filter, reduce 등)
- 함수의 조합이 쉬워진다. (작은 함수들을 조합하여 큰기능의 함수를 만드는데 적합)
- 불변성과 순수함수를 유지하는데 도움이 된다.
클로저의 활용: memoized function
React의 usememo() 나 memo()도 같은 원리로 동작한다.
예제
// 기본 팩토리얼 (재귀) 함수 - 순수하게 재귀만으로 동작한다
function factorial(n) {
if (n === 1) return 1;
return n * factorial(n - 1);
}
//메모이제이션 함수
function memoized(fn) {
const memoizedTable = {}; // 클로저로 유지되는 캐시 저장소
return function B(k) {
return memoizedTable[k] ?? (memoizedTable[k] = fn(k));
};
}
//-> B(k)를 호출할 때마다 해당 값이 있으면 그값을 반환하고, 없으면 계산을 진행하여 저장하고 그 값을 반환한다
//팩토리얼 함수에 메모이제이션을 적용한 함수
const memoizedFactorial = memoized(function A(n) {
memoizedFactorialRunCnt += 1; // 실제 실행 횟수 측정용
if (n === 1) return 1;
return n * memoizedFactorial(n - 1); // 메모이즈된 재귀 호출
});
console.log(memoizedFactorial(3)); // 계산: 3 * 2 * 1 → 저장됨
console.log(memoizedFactorial(5)); // 계산: 5 * 4 * (3 → 이미 저장돼 있음)
클로저로 상태(memoizedTable)를 기억하고, 동일 입력에 대해 함수 호출을 피하는 것이 memoization임을 예제를 통해 알 수 있다.
'Dev > Javascript' 카테고리의 다른 글
| [JS] 객체와 프로퍼티 (0) | 2025.04.10 |
|---|---|
| [JS] 피보나치 수열 구현하기 (0) | 2025.04.10 |
| [JS] 스코프 (2) | 2025.04.08 |
| [JS] 실행 컨텍스트 (2) | 2025.04.08 |
| [JS] strict mode (0) | 2025.04.08 |
