函数柯里化
柯里化概念:只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数
看一个例子:
var add = function(x) {
return function(y) {
return x + y;
};
};
var increment = add(1);
var addTen = add(10);
increment(2);
// 3
addTen(2);
// 12
我们定义了一个add
函数,它接受一个参数并返回一个新的函数。调用了add
之后,返回的函数就通过闭包的方式记住了add
的第一个参数。
因为一次性地调用它有点繁琐,所以使用一个特殊的curry
帮助函数使得这类函数的定义和调用更加容易。
curry的封装
// 初步封装
var currying = function(fn) {
// args 获取第一个方法内的全部参数
var args = Array.prototype.slice.call(arguments, 1)
return function() {
// 将后面方法里的全部参数和args进行合并
var newArgs = args.concat(Array.prototype.slice.call(arguments))
// 把合并后的参数通过apply作为fn的参数并执行
return fn.apply(this, newArgs)
}
}
这边首先是初步封装,通过闭包把初步参数给保存下来,然后通过获取剩下的arguments
(这里的arguments是函数function自带的一个隐式参数,表现为所有参数组成的数组)进行拼接,最后执行需要currying
的函数。
但是好像还有些什么缺陷,这样返回的话其实只能多扩展一个参数,currying(a)(b)(c)
这样的话,貌似就不支持了(不支持多参数调用),一般这种情况都会想到使用递归再进行封装一层。
// 支持多参数传递
function progressCurrying(fn, args) {
var _this = this
var len = fn.length;
var args = args || [];
return function() {
var _args = Array.prototype.slice.call(arguments);
Array.prototype.push.apply(args, _args);
// 如果参数个数小于最初的fn.length,则递归调用,继续收集参数
if (_args.length < len) {
return progressCurrying.call(_this, fn, _args);
}
// 参数收集完毕,则执行fn
return fn.apply(this, _args);
}
}
拓展一道经典的面试题
// 实现一个add方法,使计算结果能够满足如下预期:
// add(1)(2)(3) = 6;
// add(1, 2, 3)(4) = 10;
// add(1)(2)(3)(4)(5) = 15;
function add() {
// 第一次执行时,定义一个数组专门用来存储所有的参数
var _args = Array.prototype.slice.call(arguments);
// 在内部声明一个函数,利用闭包的特性保存_args并收集所有的参数值
var _adder = function() {
console.log("接收了一个()");
_args.push(...arguments); //...arguments在ES6中表示剩下所有的参数,接受剩下的所有参数
return _adder; //再返回adder,进行下一次递归,把上次接收的结果作为第一个参数,
//再接受下一个参数,这里相当于接受下一个()里的参数,直到接收完毕
};
// 利用toString隐式转换的特性,当最后执行时隐式转换,并计算最终的值返回
_adder.toString = function() {
// return _args.reduce(function(a, b) {
// return a + b;
// });
return _args.reduce((a, b) => a + b);
//reduce是ES5中的API,利用其能够遍历到数组的每一个元素,这里的a+b相当于当前所有的参数
}
return _adder; //这里相当于返回最后计算好的结果
}
var res = add(1, 2)(1)(2)(5);
console.log(re