JavaScript 函數(shù)柯里化
■設計思路
函數(shù)合成是把多個單一參數(shù)函數(shù)合成一個多參數(shù)函數(shù)的運算。例如,a(x)和b(x)組合為a(b(x)),則合成為f(a,b,X)。注意,這里的a⑻和b(x)都只能接受一個參數(shù)。如果接受多個參數(shù),如a(x,y)和b(a,b,c),那么函數(shù)合成就會比較麻煩。
這時就要用到函數(shù)柯里化。所謂柯里化,就是把一個多參數(shù)的函數(shù),轉(zhuǎn)化為單一參數(shù)函數(shù)。有了柯里化運算之后,我們就能做到所有函數(shù)只接受一個參數(shù)。
【實現(xiàn)方法】
先用傳遞給函數(shù)一部分參數(shù)來調(diào)用它,讓它返回一個函數(shù),然后再去處理剩下的參數(shù)。也就是說,把多參數(shù)的函數(shù)分解為多步操作的函數(shù),以實現(xiàn)每次調(diào)用函數(shù)時,僅需要傳遞更少或單個參數(shù)。例如,下面是一個簡單的求和函數(shù)add()。
var add = function (x, y) {
return x + y;
}
每次調(diào)用add(),需要同時傳入兩個參數(shù),如果希望每次僅需傳入一個參數(shù),可以這樣進行柯里化。
var add = function (x) { //柯里化
return function (y) {
return x + y
}
}
console.log(add(2)(6)); //8,連續(xù)調(diào)用
var addl = add(200);
console.log(addl(2)); //202,分步調(diào)用
函數(shù)add()接受一個參數(shù),并返回一個函數(shù),這個返回的函數(shù)可以再接受一個參數(shù),并返回兩個參數(shù)之和。從某種意義上講,這是一種對參數(shù)的“緩存”,是一種非常高效的函數(shù)式運算方法。柯里化在DOM的回調(diào)中非常有用。
■實例設計
設想curry可以接受一個函數(shù),即原始函數(shù),返回的也是一個函數(shù),即柯里化函數(shù)。這個返回的柯里化函數(shù)在執(zhí)行的過程中,會不斷地返回一個存儲了傳入?yún)?shù)的函數(shù),直到觸發(fā)了原始函數(shù)執(zhí)行的條件。例如,設計一個add()函數(shù),計算兩個參數(shù)之和。
var add = function (x, y) {
return x + y;
}
柯里化函數(shù):
var curryAdd = curry(add)
這個add需要兩個參數(shù),但是執(zhí)行curryAdd時,可以傳入更少的參數(shù),當傳入的參數(shù)少于add需要的參數(shù)時,add函數(shù)并不會執(zhí)行,curryAdd就會將這個參數(shù)記錄下來,并且返回另外一個函數(shù),這個函數(shù)可以繼續(xù)執(zhí)行傳入?yún)?shù)。如果傳入?yún)?shù)的總數(shù)等于add需要參數(shù)的總數(shù),就執(zhí)行原始參數(shù),返回想要的結(jié)果。如果沒有參數(shù)限制,最后根據(jù)空的小括號作為執(zhí)行原始參數(shù)的條件,則返回運算結(jié)果。
【實現(xiàn)代碼】
//柯里化函數(shù)
function curry(fn) {
var _argLen = fn. length; //記錄原始函數(shù)的形參個數(shù)
var _args = [].slice.call (arguments, 1); //把傳入的第2個及以后參數(shù)轉(zhuǎn)換為數(shù)組
function wrap () { //內(nèi)部函數(shù)
//把當前參數(shù)轉(zhuǎn)換為數(shù)組,與前面參數(shù)進行合并
_args = _args.concat([].slice.call(arguments));
function act() { //參數(shù)處理函數(shù)
//把當前參數(shù)轉(zhuǎn)換為數(shù)組,與前面參數(shù)進行合并
—args = _args.concat([].slice.call(arguments));
//如果傳;C參數(shù)總和大于等于原始參數(shù)的個數(shù),觸發(fā)執(zhí)行條件
if ( ( _argLen == 0 && arguments.length == 0) ||
(_argLen > 0 && _args.length >= _argLen) ) {
//執(zhí)行原始函數(shù),并把^次傳入?yún)?shù)傳入進€,返回執(zhí)行結(jié)果,停止curry return fn?apply(null, _args);
}
return arguments.callee;
}
//如果傳入?yún)?shù)大于等于原始函數(shù)的參數(shù)個數(shù),即觸發(fā)了執(zhí)行條件
if ( (_argLen == 0 && arguments.length ==0 ) ||
(_argLen > 0 && _args.length >= —argLen) ) {
//執(zhí)行原始函數(shù),并把^次傳入?yún)?shù)傳入進去,返回執(zhí)行結(jié)果,停止curry return fn.apply(null, _args);
}
act.toString = function () {//定義處理函數(shù)的字符串表示為原始函數(shù)的字符串表示 return fn.toString ();
}
return act; //返回處理函數(shù)
}
return wrap; //返回 curry 函數(shù)
【應用代碼】
應用函數(shù)無形參限制。
設計求和函數(shù),沒有形參限制,柯里化函數(shù)將根據(jù)空小括號作為最后調(diào)用原始函數(shù)的條件。
//求和函數(shù),參數(shù)不限
var add= function () {//把參數(shù)轉(zhuǎn)換為數(shù)組,然后調(diào)用數(shù)組的reduce方法
//迭代所有參數(shù)值,返回最后匯總的值
return [].slice.call(arguments).reduce(function (a, b) {
//如果元素的值為數(shù)值,則參與求和運算,否則設置為0,跳過非數(shù)字的值
return (typeof a == "number" ? a : 0) + (typeof b == "mumber" ? b : 0);
})
}
//柯里化函數(shù)
var curried = curry(add);
console.log(curried(1)(2)(3)()); //6
var curried = curry(add);
console.log(curried(1, 2, 3)(4)()); //10
var curried = curry(add,1);
console.log(curried(1, 2)(3)(3)()); //10
var curried = curry(add,1,5);
console.log(curried(1, 2, 3, 4)(5)()); //21
應用函數(shù)有形參限制。
設計求和函數(shù),返回3個參數(shù)之和。
var add = function (a,b,c) { //求和函數(shù),3個參數(shù)之和
return a + b+c;
}
//柯里化函數(shù)
var curried = curry(add,2)
console.log(curried(1)(2)); //5
var curried = curry(add,2,1)
console.log(curried(2)); //5
var curried = curry(add)
console.log(curried(1)(2)(6)); //9
var curried = curry(add)
console.log(curried(1, 2, 6)); //9
點擊加載更多評論>>