成都網(wǎng)站制作公司湘潭網(wǎng)站設(shè)計(jì)
文章目錄
- 基本概念
- 柯里化(Currying)是什么?
- 通用的柯里化實(shí)現(xiàn)
- ES5 實(shí)現(xiàn)
- ES6 實(shí)現(xiàn)
基本概念
在講柯里化之前我們先來(lái)了解一些基本概念:
Function.length: length 屬性指明函數(shù)的形參個(gè)數(shù)
function func1() {}
function func2(a, b) {}console.log(func1.length); // 0
console.log(func2.length); // 2
Arguments 對(duì)象: 是一個(gè)對(duì)應(yīng)于傳遞給函數(shù)的參數(shù)的類(lèi)數(shù)組對(duì)象
- arguments 對(duì)象是所有(非箭頭)函數(shù)中都可用的局部變量
- “類(lèi)數(shù)組”意味著 arguments 有長(zhǎng)度屬性,并且屬性的索引是從零開(kāi)始的,但是它沒(méi)有 Array的內(nèi)置方法,例如 forEach() 和 map()都是沒(méi)有的
- arguments.length:本次函數(shù)調(diào)用時(shí)傳入函數(shù)的實(shí)參數(shù)量(這個(gè)數(shù)字可以比形參數(shù)量大,也可以比形參數(shù)量小)
- arguments 可以被轉(zhuǎn)換為一個(gè)真正的 Array
// arguments 轉(zhuǎn)換為一個(gè)真正的 Array
var args = Array.prototype.slice.call(arguments);
var args = [].slice.call(arguments);// ES6
const args = Array.from(arguments);
const args = [...arguments];
這里小提一下: How does Array.prototype.slice.call
work?
.call()和.apply()方法允許您在函數(shù)中手動(dòng)設(shè)置this的值。因此,如果我們將.slice()中的this的值設(shè)置為一個(gè)類(lèi)似數(shù)組的對(duì)象,.slice()將假設(shè)它正在處理一個(gè)數(shù)組,并將執(zhí)行它的任務(wù)。
剩余參數(shù):允許我們將一個(gè)不定數(shù)量的參數(shù)表示為一個(gè)數(shù)組
如果函數(shù)的最后一個(gè)命名參數(shù)以…為前綴,則它將成為一個(gè)由剩余參數(shù)組成的真數(shù)組,其中從0(包括)到theArgs.length(排除)的元素由傳遞給函數(shù)的實(shí)際參數(shù)提供
function sum1(...theArgs) {console.log(theArgs)
}sum1(1, 2, 3); // [1, 2, 3]function sum2(a, ...theArgs) {console.log(theArgs)
}sum2(1, 2, 3); // [2, 3]
注:剩余參數(shù)只包含那些沒(méi)有對(duì)應(yīng)形參的實(shí)參,而 arguments 對(duì)象包含了傳給函數(shù)的所有實(shí)參。
柯里化(Currying)是什么?
柯里化,是把接受多個(gè)參數(shù)的函數(shù)變換成接受一個(gè)單一參數(shù)(最初函數(shù)的第一個(gè)參數(shù))的函數(shù),并且返回接受余下的參數(shù)而且返回結(jié)果的新函數(shù)的技術(shù)。核心思想是把多參數(shù)傳入的函數(shù)拆成單參數(shù)(或部分)函數(shù),內(nèi)部再返回調(diào)用下一個(gè)單參數(shù)(或部分)函數(shù),依次處理剩余的參數(shù)。
舉個(gè)例子:
// 傳統(tǒng)寫(xiě)法
function sum(a, b, c) {return a + b + c
}
console.log(sum(1,2,3)); // 6// 柯里化
function sum(a) {return function (b) {return function (c) {return a + b + c}}
}
console.log(sum(1)(2)(3)); // 6// 將 a + b + c 的操作提取成一個(gè)方法
function curry(fn) {return function (a) {return function (b) {return function (c) {return fn(a, b, c);};};}
}function add(a, b, c) {return a + b + c;
}
var sum = curry(add);
console.log(sum(1)(2)(3)); // 6
這樣就對(duì)一個(gè)函數(shù)實(shí)現(xiàn)了柯里化。但這個(gè) curry 函數(shù)并不通用,實(shí)際開(kāi)發(fā)過(guò)程中參數(shù)數(shù)量具有不確定性:比如 sum(1)(2,3) 或 sum(1,2)(3) ,或是一個(gè)函數(shù)需要傳 n 個(gè)參數(shù),如下:
function curry(fn) {return function (a1) {return function (a2) {return function (a3) {//......return function (aN) {return fn(a1, a2, a3, ...aN);};};};};
}
我們需要實(shí)現(xiàn)一個(gè)通用的柯里化函數(shù),對(duì)于參數(shù)的不確定性,我們可以通過(guò)使用 arguments 和 遞歸 來(lái)處理。
通用的柯里化實(shí)現(xiàn)
在下面實(shí)現(xiàn)代碼中所用到的一些屬性、對(duì)象、方法我們已在基本概念中介紹過(guò),方便大家理解。
ES5 實(shí)現(xiàn)
// 法一 (遞歸調(diào)用 judge)
function curry(fn) {return function judge() {// arguments 轉(zhuǎn)換為數(shù)組var _args = Array.prototype.slice.call(arguments);// 判斷首次(或組合后)所傳的參數(shù)數(shù)量 是否小于 fn 方法的形參個(gè)數(shù)if (_args.length < fn.length) {// 首次(或組合后)所傳的參數(shù)數(shù)量 小于 fn 方法的形參個(gè)數(shù), 內(nèi)部再返回調(diào)用下一個(gè)單參數(shù)(或部分)函數(shù)return function (){// 獲取當(dāng)前參數(shù)數(shù)組var _args2 = Array.prototype.slice.call(arguments);// 將之前所傳的參數(shù)和當(dāng)前的參數(shù)組合,遞歸調(diào)用return judge.apply(this, _args.concat(_args2))};} else {// 首次(或組合后)所傳的參數(shù)數(shù)量 等于 fn 方法的形參個(gè)數(shù),則直接調(diào)用 fn 函數(shù)return fn.apply(this, _args);}}
}// 法二 (遞歸調(diào)用 curry)
function curry(fn, args) {var args = args || []; // 首次為 []return function () {var _args = args.concat([].slice.call(arguments));if (_args.length < fn.length) {return curry.call(this, fn, _args);} else {return fn.apply(this, _args);}}
}function add(a, b ,c) {return a + b + c;
}const addCurry = curry(add);console.log(addCurry(1,2)(3)) // 6console.log(addCurry(1)(2)(3)) // 6console.log(addCurry(1)(2,3)) // 6
簡(jiǎn)而言之,就是將所有實(shí)際的傳參組合起來(lái),如果實(shí)際參數(shù)個(gè)數(shù)等于 fn 方法的形參個(gè)數(shù)后便調(diào)用 fn 方法。
ES6 實(shí)現(xiàn)
// 法一 (遞歸調(diào)用 judge)
const curry = (fn) => {return function judge(...args) {if (args.length < fn.length) {return function (...args2) {return judge(...args, ...args2);};}return fn(...args);};
}// 法二 (遞歸調(diào)用 curry)
const curry = (fn, ...arg) => (arg.length >= fn.length ? fn(...arg) : (..._arg) => curry(fn, ...arg, ..._arg))function add(a, b ,c) {return a + b + c;
}const addCurry = curry(add);console.log(addCurry(1,2)(3)) // 6console.log(addCurry(1)(2)(3)) // 6console.log(addCurry(1)(2,3)) // 6
想要明白柯里化函數(shù),還是建議大家動(dòng)手寫(xiě)寫(xiě),在寫(xiě)的過(guò)程中分析執(zhí)行步驟,方便理解。