高阶函数
至少满足以下条件的函数:
- 接受一个或多个函数作为输入
- 输出一个函数
JS高阶函数浅析
柯里化(Currying)
什么是柯里化
柯里化是函数式编程中的一种进阶技巧。
柯里化的直接表现形式就是,当我们有一个函数f(a,b,c),通过柯里化转换,使得这个函数可以被这样调用f(a)(b)(c)。
柯里化有什么用途?
- 参数可以复用,便于封装语法糖
例如:系统中经常有基础的log方法,可以记录不同时间的程序运行信息,记录具体的日志信息。我们假设这个log方法调用一次,就向后台更新一条log记录。
function log(date, importance, message) {
$.post(`[${date.getHours()}:${date.getMinutes()}] [${importance}] ${message}`);
}
比如我们现在要记录当天的日志信息:
log(new Date(), "DEBUG", "some debug");
log(new Date(), "DEBUG2", "some debug3");
我们会发现其实第一个参数是不变的,只有后面的其他参数是变化的。
有了currying,我们可以这样去调用:
let curryLog = curry(log);
let logNow = curryLog(new Date());
logNow("DEBUG2", "some debug3");
就减少了很多冗余代码。语义也更加清晰。
一步一步实现Currying function
当我们定义一个普通的函数,分别传入参数,我们可以得到参数相加的结果。
function sum(a,b){
return a+b;
}
sum(1,2);//3
现在我们希望可以把sum方法转换一下,可以这样被调用:
sum(1,2);//3
sum(1)(2);//3
初始实现思路
- arguments来保存参数
因为传入的参数数量是不确定的,因此我们第一个思路就是借助JS当中的arguments对象来实现。 - 如果传入的参数的个数小于被柯里化的函数定义的形式参数的个数,那需要把传入的参数保留下来,并且要返回函数可以继续接收下一个参数。
递归实现法
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是传入的实参的数量
//fn.length是函数形参的数量
if (arguments.length < len) {
// 如果被柯里化后的函数调用时,实参的数量少于fn形参的数量
var combined = [fn].concat(slice.call(arguments));
// 就需要把参数保存下来,并且能够继续返回一个函数,接收后续的参数
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出的结果我们可以看出来,每次调用的参数都被保存起来了。
curry函数中,length参数作为递归结束的条件,每保存一个参数,就把长度相应减少。
curried
函数用来返回嵌套的函数,通过组合内层和外层的参数,把参数保留起来。
当递归结束的时候,arguments里面就包含了所有的参数。
JavaScript专题之函数柯里化
js-info_Currying
循环实现法
function curry(fn, args) {
var len = fn.length;
args = args || [];
return function() {
var _args = args.slice(0);
//把所有arguments保存下来
_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参数解法
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));
}
}
};
}