目錄
HOF是什麼
Higher Order Functions(HOF)是一種functional programming的技巧,它是一個接收function,並回傳function的function
const nums = [1,2,3,4,5,6,70,8];
// 陣列的filter是JS內建的HOF
nums.filter((n) => n > 10);
const filter = (fn) => {
return (arr) => {
return fn(arr)
};
}
// currying和partial也是HOF
const curriedSum = (n1) => {
return (n2) => {
return n1 + n2;
}
}
// filterLarger100也是HOF
const filterLarger100 = filter((arr) => {
const res = [];
arr.forEach((i) => {
if(i > 100){
res.push(i);
}
})
return res;
});
filterLarger100([12,33,120,11]);
巧妙運用的話HOF甚至可以形成一條chain
(pipeline function)
為什麼要用HOF
HOF的核心概念是組合
(composition),以提高複用性
(reusability)
也就是把每個function根據
各自的職責拆開
來、將共用的部分抽象化
,以便重複使用,使用時
再根據需求將function組合
再一起
舉例來說,如果我們需要計算數字的加、減
在不使用HOF的情況可能會長這樣,或者是一個function內有判斷式,利用判斷式決定要執行哪段code
const increaseNums = (data, operatedNum) => {
return data.map((d) => d + operatedNum);
}
const decreaseNums = (data, operatedNum) => {
return data.map((d) => d - operatedNum);
}
console.log(increaseNums(nums, 1)); // [11, 21, 51]
console.log(decreaseNums(nums, 20)); // [-10, 0, 30]
使用HOF的話則會像以下這樣
type CalculateFunction = (originalNumber:number, operatedNumber:number) => number;
// HOF + partial
const calculate = (calculateFn:CalculateFunction) => {
return (data:number[], operatedNum:number) => {
return data.map((d) => calculateFn(d, operatedNum));
}
}
const increaseNums = calculate(increase);
const decreaseNums = calculate(decrease);
console.log(increaseNums(nums, 1)); // [11, 21, 51]
console.log(decreaseNums(nums, 20)); // [-10, 0, 30]
compose
compose function會接收一個或多個function,然後由左而右將function組合起來,其中最左邊的function可以接收任意數量的參數
一但最左側的functionc回傳了值
,其右邊
的function會接收
這個回傳值,並進行一些計算後再回傳另一個值
,直到所有
function都跑過
type CalculateFunction = (originalNumber:number, operatedNumber?:number) => number;
const compose = (fn:CalculateFunction, ...fns:CalculateFunction[]) => {
return (...num: number[]) => {
// 迭代第二個function開始的一連串陣列
return fns.reduce((res, currFn, index) => {
// 將前一個function的回串值作為第一個引數,傳給目前的function做計算,第二個引數為index + 2的元素是因為前面兩已經用在第一個function了
return currFn(res, num[index + 2]);
}, fn.apply(this, [num[0], num[1]])); // 使用apply呼叫第一個function,並把前兩個參數傳進去,將其回傳值作為初始值
}
}
用起來會像這樣
const add = (a:number, b:number = 0) => a + b;
const devide = (a:number, b:number = 0) => a - b;
const multiply = (a:number, b:number = 1) => a * b;
const halfOff = (a:number) => a / 2;
const square = (a:number) => Math.pow(a, 2);
const getSquareRoot = (a: number) => (a) => Math.sqrt(a);
// 上底加下底、乘以高、除以2
const getTrapezoidArea = compose(add, multiply, halfOff);
// 底乘以高、除以2
const getTriangleArea = compose(multiply, halfOff);
// 短邊平方加上長邊平方、開根號
const getHypotenuseOfTriangle = compose((a, b = 1) => suqare(a) + square(b), getSquareRoot);
// 加總
const getSum = compose(add, add, add, add);
// 乘總
const getProduct = compose(multiply, multiply, multiply);
console.log(getTrapezoidArea(5, 7, 6)); // 36
console.log(getTriangleArea(7, 6)); // 21
console.log(getHypotenuseOfTriangle(3, 4)); // 5
console.log(getSum(1, 2, 3, 4, 5)); // 15
console.log(getProduct(2, 2, 2, 2)); // 16
參考資料
Functional Programming — Higher Order Function, HOF
Higher-Order Functions(HoF) in JavaScript - Explain Like I'm Five
Compose and Pipe in JavaScript