高階函數(shù)
至少滿足以下條件的函數(shù):
- 接受一個(gè)或多個(gè)函數(shù)作為輸入
- 輸出一個(gè)函數(shù)
JS高階函數(shù)淺析
柯里化(Currying)
什么是柯里化
柯里化是函數(shù)式編程中的一種進(jìn)階技巧踱蠢。
柯里化的直接表現(xiàn)形式就是焙格,當(dāng)我們有一個(gè)函數(shù)f(a,b,c),通過(guò)柯里化轉(zhuǎn)換崖瞭,使得這個(gè)函數(shù)可以被這樣調(diào)用f(a)(b)(c)狱杰。
柯里化有什么用途啊掏?
- 參數(shù)可以復(fù)用暖混,便于封裝語(yǔ)法糖
例如:系統(tǒng)中經(jīng)常有基礎(chǔ)的log方法莲镣,可以記錄不同時(shí)間的程序運(yùn)行信息,記錄具體的日志信息残拐。我們假設(shè)這個(gè)log方法調(diào)用一次途茫,就向后臺(tái)更新一條log記錄。
function log(date, importance, message) {
$.post(`[${date.getHours()}:${date.getMinutes()}] [${importance}] ${message}`);
}
比如我們現(xiàn)在要記錄當(dāng)天的日志信息:
log(new Date(), "DEBUG", "some debug");
log(new Date(), "DEBUG2", "some debug3");
我們會(huì)發(fā)現(xiàn)其實(shí)第一個(gè)參數(shù)是不變的溪食,只有后面的其他參數(shù)是變化的囊卜。
有了currying,我們可以這樣去調(diào)用:
let curryLog = curry(log);
let logNow = curryLog(new Date());
logNow("DEBUG2", "some debug3");
就減少了很多冗余代碼错沃。語(yǔ)義也更加清晰栅组。
一步一步實(shí)現(xiàn)Currying function
當(dāng)我們定義一個(gè)普通的函數(shù),分別傳入?yún)?shù)枢析,我們可以得到參數(shù)相加的結(jié)果笑窜。
function sum(a,b){
return a+b;
}
sum(1,2);//3
現(xiàn)在我們希望可以把sum方法轉(zhuǎn)換一下,可以這樣被調(diào)用:
sum(1,2);//3
sum(1)(2);//3
初始實(shí)現(xiàn)思路
- arguments來(lái)保存參數(shù)
因?yàn)閭魅氲膮?shù)數(shù)量是不確定的登疗,因此我們第一個(gè)思路就是借助JS當(dāng)中的arguments對(duì)象來(lái)實(shí)現(xiàn)排截。 - 如果傳入的參數(shù)的個(gè)數(shù)小于被柯里化的函數(shù)定義的形式參數(shù)的個(gè)數(shù),那需要把傳入的參數(shù)保留下來(lái)辐益,并且要返回函數(shù)可以繼續(xù)接收下一個(gè)參數(shù)断傲。
遞歸實(shí)現(xiàn)法
function sum(a, b,c) {
return a + b + c;
}
function curried(fn) {
var slice = Array.prototype.slice;
var outer = slice.call(arguments,1);
return function(){
var inner = slice.call(arguments);
var args = outer.concat(inner);
console.log('args',args);
return fn.apply(this,args);
}
}
function curry(fn,length) {
var slice = Array.prototype.slice;
let len = length | fn.length;
return function () {
// arguments.length是傳入的實(shí)參的數(shù)量
//fn.length是函數(shù)形參的數(shù)量
if (arguments.length < len) {
// 如果被柯里化后的函數(shù)調(diào)用時(shí),實(shí)參的數(shù)量少于fn形參的數(shù)量
var combined = [fn].concat(slice.call(arguments));
// 就需要把參數(shù)保存下來(lái)智政,并且能夠繼續(xù)返回一個(gè)函數(shù)认罩,接收后續(xù)的參數(shù)
console.log('combined', combined);
var sub = curried.apply(this, combined);
console.log('curried', sub);
return curry(sub, len - arguments.length);
} else {
return fn.apply(this,arguments);
}
}
}
var currySum = curry(sum);
console.log(currySum(1)(2)(3));
// output:
//combined (2) [?, 1]
//curried ? (){
// var inner = slice.call(arguments);
// var args = outer.concat(inner);
// console.log('args',args);
// return fn.apply(this,args);
// }
// combined (2) [?, 2]
// curried ? (){
// var inner = slice.call(arguments);
// var args = outer.concat(inner);
// console.log('args',args);
// return fn.apply(this,args);
// }
// args (2) [2, 3]
// args (3) [1, 2, 3]
// 6
從console出的結(jié)果我們可以看出來(lái),每次調(diào)用的參數(shù)都被保存起來(lái)了续捂。
curry函數(shù)中垦垂,length參數(shù)作為遞歸結(jié)束的條件宦搬,每保存一個(gè)參數(shù),就把長(zhǎng)度相應(yīng)減少劫拗。
curried
函數(shù)用來(lái)返回嵌套的函數(shù)间校,通過(guò)組合內(nèi)層和外層的參數(shù),把參數(shù)保留起來(lái)页慷。
當(dāng)遞歸結(jié)束的時(shí)候憔足,arguments里面就包含了所有的參數(shù)。
JavaScript專題之函數(shù)柯里化
js-info_Currying
循環(huán)實(shí)現(xiàn)法
function curry(fn, args) {
var len = fn.length;
args = args || [];
return function() {
var _args = args.slice(0);
//把所有arguments保存下來(lái)
_args = _args.concat(Array.prototype.slice.call(arguments));
if (_args.length < len) {
return curry.call(this, fn, _args);
}
else {
return fn.apply(this, _args);
}
}
}
Rest參數(shù)解法
function curry(func) {
return function curried(...args) {
if (args.length >= func.length) {
return func.apply(this, args);
} else {
return function(...args2) {
return curried.apply(this, args.concat(args2));
}
}
};
}