柯里化(Currying)是一种函数转化方法,是高阶函数(接收函数作为参数的函数)的一 种,它将一个接收多参数的函数转化为接收部分参数的函数。柯里化后的函数只传 递部分参数来调用,并返回一个新的函数,去处理剩余的参数,是逐步传参的过程。
举个?:
function add(x,y) {
return x+y;
}
add(2,3);//返回值是3
上面代码可以转换成:
function add(x) {
return function(y) {
return x+y;
}
}
add(1)(2);//返回值是3
一个常见的面试题,用柯里化的方式实现一个函数,使add(1,2,3);add(1)(2,3);add(1)(2)(3);返回的结果与
function add(x,y,z) {
return x+y+z
}
的返回结果一致。
function add(x,y,z) {
return x+y+z
}
function curry(fn, args) {
//fn.length:形参个数,num为固定值
var num = fn.length;
var args = args || [];
return function() {
//arr用于收集传入的参数
var arr =[].slice.call(arguments);
//将之前收集的参数加入arr
[].push.apply(arr, args);
//若参数小于num,递归调用继续收集参数
if (arr.length < num) {
return curry.call(this, fn, arr);
}
//参数达到num,则执行fn
return fn.apply(this, arr);
}
}
const _add = curry(add);
_add(1,2,3);//6
_add(1)(2,3);//6
_add(1)(2)(3);//6
代码解析:
上面核心函数curry中频繁的使用了call和apply函数,下面逐条解析代码含义
1.
let arr =[].slice.call(arguments);
arguments是curry函数return的function所接收的参数,如上例中_add(1,2,3)中的1,2,3就是arguments。arguments是一个类数组,类数组,顾名思义,即很像数组,但不是数组。
function add(x,y){
console.log(arguments);
return x+y;
}
上述代码执行add(1,2);打印的arguments的值如下图:
其本质是一个对象,所以通过arguments[0]可以取到第一个参数1,arguments[1]可以取到第二个参数2.
slice是数组的一个方法,可以获取目标数组内指定元素,可以接收0-2个参数。不接收参数时,可以获取到整个目标数组。如:
let arr = [1,2,3];
let result = arr.slice();
console.log(result);
上述代码中result的结果为:[1,2,3]
[].slice.call()相当于Array.prototype.call()。[].slice.call(arguments)即通过call方法改变this指向,使arguments可以使用数组中的slice方法,可以理解为arguments.slice(),即let arr =[].slice.call(arguments),arr的值其实是将arguments参数转化为一个数组;
2.
[].push.apply(arr, args);
上述代码的用意是将args参数push到arr数组中,那么为什么没有直接使用arr.push(args)?因为在args定义的时候有这样一句:
let args = args || [];
也就是说,args可能是curry传进来的参数,如果没有传入参数,那么就初始化成一个数组,当args是数组的时候,arr.push(args),就是向arr中push进一个数组,那么arr就变成了二维数组了。但代码的本意是使用arr保存传进来的参数,因此不能直接push。
[].push.apply(arr, args),通过apply的参数特性,将args数组中的元素push到arr当中,如args的值是[1,2,3],[].push.apply(arr, args)相当于arr.push(1,2,3)。
3.
if (arr.length < num) {
return curry.call(this, fn, arr);
}
curry.call(this, fn, arr),相当于递归调用,再调用一遍自己(curry函数),同时传入两个参数,fn和arr。
4.
return fn.apply(this, arr);
fn.apply(this, arr)即调用fn函数,arr做为参数。
以上是对实现一个柯里化函数的分解式讲解。