寫在前面
此系列來源于開源項(xiàng)目:前端 100 問:能搞懂 80%的請把簡歷給我
為了備戰(zhàn) 2021 春招
每天一題,督促自己
從多方面多角度總結(jié)答案,豐富知識
請實(shí)現(xiàn)一個 add 函數(shù),滿足以下功能。
簡書整合地址:前端 100 問
正文回答
題目
add(1); // 1
add(1)(2); // 3
add(1)(2)(3);// 6
add(1)(2, 3); // 6
add(1, 2)(3); // 6
add(1, 2, 3); // 6
回答
function add() {
let args = [].slice.call(arguments);
let fn = function () {
let fn_args = [].slice.call(arguments);
return add.apply(null, args.concat(fn_args));
};
fn.toString = function () {
return args.reduce((a, b) => a + b);
};
return fn;
}
擴(kuò)展
高階函數(shù)
高階函數(shù)英文叫 Higher-order function,它的定義很簡單,就是至少滿足下列一個條件的函數(shù):
- 接受一個或多個函數(shù)作為輸入
- 輸出一個函數(shù)
也就是說高階函數(shù)是對其他函數(shù)進(jìn)行操作的函數(shù),可以將它們作為參數(shù)傳遞,或者是返回它們。 簡單來說,高階函數(shù)是一個接收函數(shù)作為參數(shù)傳遞或者將函數(shù)作為返回值輸出的函數(shù)。
函數(shù)作為參數(shù)傳遞
JavaScript 語言中內(nèi)置了一些高階函數(shù),比如 Array.prototype.map,Array.prototype.filter 和 Array.prototype.reduce,它們接受一個函數(shù)作為參數(shù),并應(yīng)用這個函數(shù)到列表的每一個元素。我們來看看使用它們與不使用高階函數(shù)的方案對比。
Array.prototype.map
map() 方法創(chuàng)建一個新數(shù)組,其結(jié)果是該數(shù)組中的每個元素都調(diào)用一個提供的函數(shù)后返回的結(jié)果,原始數(shù)組不會改變。傳遞給 map 的回調(diào)函數(shù)(callback)接受三個參數(shù),分別是 currentValue、index(可選)、array(可選),除了 callback 之外還可以接受 this 值(可選),用于執(zhí)行 callback 函數(shù)時使用的 this 值。
來個簡單的例子方便理解,現(xiàn)在有一個數(shù)組 [1, 2, 3, 4],我們想要生成一個新數(shù)組,其每個元素皆是之前數(shù)組的兩倍,那么我們有下面兩種使用高階和不使用高階函數(shù)的方式來實(shí)現(xiàn)。
// 不使用高階函數(shù)
// 木易楊
const arr1 = [1, 2, 3, 4];
const arr2 = [];
for (let i = 0; i < arr1.length; i++) {
arr2.push(arr1[i] * 2);
}
console.log(arr2);
// [2, 4, 6, 8]
console.log(arr1);
// [1, 2, 3, 4]
// 使用高階函數(shù)
// 木易楊
const arr1 = [1, 2, 3, 4];
const arr2 = arr1.map((item) => item * 2);
console.log(arr2);
// [2, 4, 6, 8]
console.log(arr1);
// [1, 2, 3, 4]
Array.prototype.filter
略
Array.prototype.reduce
略
函數(shù)作為返回值輸出
isType 函數(shù)
我們知道在判斷類型的時候可以通過 Object.prototype.toString.call 來獲取對應(yīng)對象返回的字符串,比如:
let isString = (obj) =>
Object.prototype.toString.call(obj) === "[object String]";
let isArray = (obj) => Object.prototype.toString.call(obj) === "[object Array]";
let isNumber = (obj) =>
Object.prototype.toString.call(obj) === "[object Number]";
可以發(fā)現(xiàn)上面三行代碼有很多重復(fù)代碼,只需要把具體的類型抽離出來就可以封裝成一個判斷類型的方法了,代碼如下。
let isType = (type) => (obj) => {
return Object.prototype.toString.call(obj) === "[object " + type + "]";
};
isType("String")("123"); // true
isType("Array")([1, 2, 3]); // true
isType("Number")(123); // true
這里就是一個高階函數(shù),因?yàn)?isType 函數(shù)將 obj => { ... } 這一函數(shù)作為返回值輸出。
回到題目本身
我們知道打印函數(shù)時會自動調(diào)用 toString()方法,函數(shù) add(a) 返回一個閉包 sum(b),函數(shù) sum() 中累加計算 a = a + b,只需要重寫 sum.toString()方法返回變量 a 就可以了。
function add(a) {
function sum(b) { // 使用閉包
a = a + b; // 累加
return sum;
}
sum.toString = function() { // 重寫toString()方法
return a;
}
return sum; // 返回一個函數(shù)
}
add(1); // 1
add(1)(2); // 3
add(1)(2)(3); // 6
add(1)(2)(3)(4); // 10