柯里化,第一次听这个词的时候一脸懵逼,百度上看了一些大佬的文章,在这里和大家一起学习一下什么是柯里化。
官方解释:柯里化,英文currying,是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。
说的还是很难懂啊,直接上个简单🌰吧,就能明白了,拿我们最常见的add函数
//正常add(x,y)函数
function add(x,y){
return x+y;
}
//柯里化后的函数
function curryAdd(x){
return function(y){
return x+y;
}
}
console.log(add(1,2));//3
console.log(curryAdd(1)(2));//3
从上面例题我们可以看出来,就是函数把参数变成了先用一个函数接收部分参数,然后返回一个函数去处理剩余的参数。
直接敲代码吧,奉上封装好的通用方法:
1.简单版
function curry(fn){
var args =Array.prototype.slice.call(arguments,1);
return function(){
var _args = Array.prototype.slice.call(arguments);//新的参数
var newArgs = args.concat(_args);
return fn.apply(this,newArgs);//apply不要忘记哦~~
}
}
function add(a, b) {
return a + b;
}
var f1 = curry(add,1,2);
console.log(f1()); //3
var f2 = curry(add,1);
console.log(f2(2)); //3
var f3 = curry(add);
console.log(f3(1, 2)) // 3
emm,这一般是简单版,不能链式调用,也就是如f3(1)(2);
废话不多说,直接上加强版
2.加强版
function curry(fn){
var args = Array.prototype.slice.call(arguments,1);
var len = fn.length;//参数长度
return function(){
var _args = Array.prototype.slice.call(arguments);//新的参数
var newArgs = args.concat(_args);//将所有参数集合在一起
if(newArgs.length<len){
//判断当前函数的参数和期望函数参数
return curry.call(this,fn,...newArgs);//如果不够,递归调用
}else{
return fn.apply(this,newArgs);;//如果够,执行函数
}
}
}
function add(x,y){
return x+y;
}
var f1 = curry(add)
console.log(f1(2)(1));//3
var f2 = curry(add,1);
console.log(f2(2));//3
var f3 = curry(add,1,2);
console.log(f3());//3
柯里化有什么好处呢?
柯里化有三个常见的作用:
详细可以参考这一篇文章JS中的柯里化(currying
- 参数复用;
- 提前返回;
- 延迟计算/运行。
bind实现柯里化:
具体可以看我写过的这篇文章bind的运用和bind的实现
最后跟我一起看一道经典的curry面试题
实现一个add方法,使计算结果能够满足如下预期:
add(1)(2)(3) = 6;
add(1, 2, 3)(4) = 10;
add(1)(2)(3)(4)(5) = 15;
上面写的通用版,只针对有特定参数个数的函数适用,所以这道题还要重新思考一下。
由于这道题传入的参数个数不固定,这里就需要使用函数的toString来完成。
- 法一:
function add(){
var args = Array.prototype.slice.call(arguments);
var fn = function(){
//直接利用闭包的特性保存args并收集所有的参数值
args.push(...arguments);//es6写法,
return add(...args);//es6写法
// es5写法 add.apply(null,args);
//也可以直接返回fn
//return fn;
}
// 利用隐式转换的特性,当最后执行时隐式转换,并计算最终的值返回
fn.toString = function(){
return args.reduce(function(a,b){
return a+b;
})
}
return fn;
}
console.log(add(1)(2)(3) );//6
console.log(add(1, 2, 3)(4)); //10
console.log(add(1)(2)(3)(4)(5));//15
- 法二:
function add(){
var args = Array.prototype.slice.call(arguments);
var fn = function(){
var _args = Array.prototype.slice.call(arguments);
var newArgs = args.concat(_args);//参数拼接
return add.apply(null,newArgs);//es5写法
// es6写法 add(...newArgs);
}
fn.toString = function(){
return args.reduce(function(a,b){
return a+b;
})
}
return fn;
}
console.log(add(1)(2)(3) );//6
console.log(add(1, 2, 3)(4)); //10
console.log(add(1)(2)(3)(4)(5));//15
其实方法一和方法二都是一样的,只不过是有一些es5和es6上写法差别,就分开写了。
这里说一下为啥需要使用函数的toString
当我们执行函数,直接参与计算的时候,会隐式调用toString,直接将函数体转换为字符串参与计算。
function a(){return 10};
console.log(a+20);//function a(){return 10}20
但是如果我们重写toString方法,函数参与计算时候,就可以输出我们想要的结果了。
function a(){return 10};
a.toString = function(){
return 20;
}
console.log(a+20);//40
现在应该是可以理解了吧~~~就不多说了。
网友评论