일급 함수 II
- 11.1 카피-온-라이트 리팩터링
- 11.2 함수를 리턴하는 함수
- 11.3 카피-온-라이트에 적용하기
- 11.4 방어적 복사와 조합
기존 함수의 동작을 변경하지 않고, 새로운 동작을 추가한 함수를 만드는 것 — 이게 "함수를 리턴하는 함수" 패턴의 핵심이야.
10장에서 일급 함수와 고차 함수의 기본을 배웠으니, 11장에서는 이걸 실전에 적용해. 6장에서 만들었던 카피-온-라이트 함수들을 떠올려 봐 — setPriceByName, setQuantityByName 같은 거. 거의 같은 코드인데 setPrice와 setQuantity만 달랐잖아. 10장에서 배운 "본문을 콜백으로 바꾸기"를 적용하면:
function modifyItemByName(cart, name, modify) {
var cartCopy = cart.slice();
for (var i = 0; i < cartCopy.length; i++) {
if (cartCopy[i].name === name)
cartCopy[i] = modify(cartCopy[i]);
}
return cartCopy;
}
// 사용
var cart = modifyItemByName(cart, "shoes", function(item) {
return setPrice(item, 13);
});
카피-온-라이트 패턴도 더 일반화할 수 있어. 배열이나 객체를 복사하고 수정하는 패턴 자체를 추상화하는 거지:
function withArrayCopy(array, modify) {
var copy = array.slice();
modify(copy); // 복사본을 변경 (원본은 안전)
return copy;
}
// 사용
var sortedArray = withArrayCopy(array, function(copy) {
copy.sort(); // 파괴적 연산을 안전하게 사용
});
Array.sort() 같은 파괴적(mutating) 메서드도 withArrayCopy 안에서 쓰면 카피-온-라이트가 돼. 복사본만 변경하니까.
10장에서는 함수를 인자로 받는 고차 함수를 봤는데, 이번에는 함수를 리턴하는 고차 함수야. 7장에서 방어적 복사 코드를 떠올려 봐 — 방어적 복사를 할 때마다 deepCopy를 앞뒤로 감싸야 했잖아. 이 패턴이 반복되면 실수하기 쉬운데, 함수를 리턴하는 함수로 이 패턴을 감싸면:
function wrapWithDeepCopy(f) {
return function(arg) {
var copy = deepCopy(arg);
var result = f(copy);
return deepCopy(result);
};
}
var safeAddItem = wrapWithDeepCopy(add_item);
// safeAddItem을 호출하면 자동으로 deepCopy가 적용됨
wrapWithDeepCopy는 함수 f를 받아서, f를 방어적 복사로 감싼 새로운 함수를 리턴해.
이런 식으로 자바스크립트의 파괴적 배열 메서드들을 한번에 안전하게 만들 수도 있어:
var push = wrapWithCopyOnWrite(Array.prototype.push);
var pop = wrapWithCopyOnWrite(Array.prototype.pop);
var sort = wrapWithCopyOnWrite(Array.prototype.sort);
실제로 이렇게 범용적으로 만들려면 엣지 케이스가 많지만, 아이디어 자체가 중요해 — 반복되는 패턴을 고차 함수로 자동화할 수 있다는 것.
로깅 추가도 같은 패턴이야:
function wrapWithLogging(f) {
return function() {
var result;
try {
result = f.apply(this, arguments);
} catch (e) {
logToServer(e);
throw e;
}
return result;
};
}
var safeCalcTotal = wrapWithLogging(calc_total);
"함수를 리턴하는 함수" 패턴은 다른 이름으로 함수 래퍼(wrapper) 또는 **데코레이터(decorator)**라고도 하거든. 원래 함수를 감싸서 부가 기능을 입히는 거야. 이 패턴을 체이닝할 수도 있어 — var safeFn = wrapWithLogging(wrapWithDeepCopy(originalFn)); 이렇게 하면 originalFn에 방어적 복사 + 로깅이 한번에 적용돼.
정리
11장 읽고 기억할 거 세 가지:
- 카피-온-라이트의 반복 패턴도 고차 함수로 추상화 가능.
withArrayCopy같은 래퍼로 파괴적 메서드를 안전하게 감싼다 - 함수를 리턴하는 함수 = 기존 함수에 동작을 추가한 새 함수를 만든다.
wrapWithDeepCopy,wrapWithLogging같은 패턴 - 래퍼 함수는 체이닝 가능. 여러 래퍼를 겹쳐서 다양한 부가 기능을 조합할 수 있다