简单粗暴详细讲解javascript实现函数柯里化
函数柯里化(黑人问号脸)???Currying(黑人问号脸)???妥妥的中式翻译既视感;下面来一起看看究竟什么是函数柯里化:
维基百科的解释是:把接收多个参数的函数变换成接收一个单一参数(最初函数的第一个参数)的函数,并返回接受剩余的参数而且返回结果的新函数的技术。其由数学家Haskell Brooks Curry提出,并以curry命名。
概念往往都是干涩且难懂的,让我们用人话来解释就是:如果我们不确定这个函数有多少个参数,我们可以先给它传入一个参数,然后通过JS闭包(如若不懂JS闭包,请先学习闭包知识点再来学习本篇博文 https://www.cnblogs.com/dengyao-blogs/p/11475575.html )来进行返回一个函数,内部函数接收除开第一个参数外的其余参数进行操作并输出,这个就是函数的柯里化;
举个小例子:
场景(需求):
如果我们需要计算一个程序员每天的加班时间,那么我们的第一反应应该是这样;
var overtime=0; function time(x){ return overtime+=x; } time(1); //1 time(2); //3 time(3); //6
上面的代码固然没有问题,可是需要每天调用都算加一下当天的时间,那么有没有可以偷懒的办法呢?有的!
function time(x){ return function(y){ return x+y; } } var times=time(0); times(3);
但是上面代码依然存在问题,在实际开发中很多时候我们的参数是不确定的,上面代码虽然简单的实现了柯里化的基本操作,但是对于参数不确定的情况是处理不了的;
我们再来把代码改建一下:
// 首先定义一个变量接收函数
var overtime = (function() {
//定义一个数组用来接收参数 var args = []; //这里运用闭包,调用外部函数返回一个内部函数 return function() {
//arguments是浏览器内置对象,专门用来接收参数
//如果参数的长度为0即没有参数的时候 if(arguments.length === 0) {
//定义变量用来累加 var time = 0;
//循环累加,用i和args的长度进行比较 for (var i = 0, l = args.length; i < l; i++) {
//进行累加操作 等价于time=time+args[i] time += args[i]; }
// 返回累加的结果 return time;
//如果arguments对象参数长度不为零,即有参数的时候 }else {
//定义的空数组添加arguments参数作为数组项,第一个参数古args作为改变this指向,第二个参数arguments把剩余参数作为数组形式添加至空数组中 [].push.apply(args, arguments); } } })(); overtime(3.5); // 第一天 overtime(4.5); // 第二天 overtime(2.1); // 第三天 //... console.log( overtime() ); // 10.1
代码经过我们的改造已经实现了功能,但是这不是一个函数柯里化的完整实现,那么我们要怎么完整实现呢?
//定义方法currying,先传入一个参数
var currying=function(fn){
//定义空数组装arguments对象的剩余参数 var args=[];
//利用闭包返回一个函数处理剩余参数 return function (){
//如果arguments的参数长度为0,即没有剩余参数 if(arguments.length===0){
//执行上面方法 return fn.apply(this,args) } console.log(arguments)
//如果arguments的参数长度不为0,即还有剩余参数
//在数组的原型对象上添加数组,apply用来更改this的指向为args
//将[].slice.call(arguments)的数组添加到原型数组上 Array.prototype.push.apply(args,[].slice.call(arguments)) //args.push([].slice.call(arguments)) console.log(args)
//这里返回的arguments.callee是返回的闭包函数,callee是arguments对象里面的一个属性,用于返回正被执行的function对象 return arguments.callee } }
//这里调用currying方法并传入add函数,结果会返回闭包内部函数 var s=currying(add);
//调用闭包内部函数,当有参数的时候会将参数逐步添加到args数组中,待没有参数传入的时候直接调用
//调用的时候支持链式操作 s(1)(2)(3)();
//也可以一次性传入多个参数
s(1,2,3);
console.log(s());
JS函数柯里化的优点:
1.可以延迟计算,即如果调用柯里化函数传入参数是不调用的,会将参数添加到数组中存储,等到没有参数传入的时候进行调用;
2.参数复用,当在多次调用同一个函数,并且传递的参数绝大多数是相同的,那么该函数可能是一个很好的柯里化候选。