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