JavaScript之函数的柯里化。

预备知识

了解柯里化之前,首先要了解一下什么是函数的绑定。
好在JavaScript已经源生给我们提供了bind()函数,用于函数的绑定。具体用法如下:
假设有一个函数fn,有一个作用域context,你需要把fn绑定到context中执行,那么你只需要

fn.bind(context)

bind的内部实现是如下

function bind(fn,context){
      return function(){
          return fn.apply(context,arguments);
      }
}

注意 arguments是内部函数的参数集,并不是bind的2个参数的集合。

柯里化

柯里化用来创建已经设置好了一个或在多个参数的函数,其基本的方法和函数绑定类似:使用一个闭包返回一个函数。
柯里化和函数绑定的区别在于,当函数被调用之时,返回的函数还需要设置一些传入的参数。下面有2个例子

function add(num1,num2){
  return num1 + num2
}

function curriedAdd(num2){
  return add(5,num2);
}

add(2,3) //5
curriedAdd(3) //8

这段代码有2个函数 add 和 curriedAdd。后者本质上是前者在num1参数始终等于5的版本,尽管curriedAdd并非柯里化函数,但其很好地展示了柯里化的概念。
柯里化概念:调用另一个函数并为它传入要柯里化的函数和必要的参数,下面是创建柯里化的通用方式:

function curry(fn){
  let args = Array.prototype.slice.call(arguments,1);
  //第一句作用是将参数数组的第一个分离出去,返回值赋值给args这个变量
  return function(){  //curry方法返回一个匿名函数
    let innerArgs = Array.prototype.slice.call(arguments);
    //将参数数组arguments赋值给变量innerArgs
    let finalArgs = args.concat(innerArgs);
    //合并args和innerArgs 并将返回值赋值给finalArgs
    return fn.apply(null,finalArgs);
    //传入finalArgs并在fn上执行
  };
}

记住这个curry函数,有必要将它纳入你个人的开发库。curry的第一个参数是要进行柯里化的函数,其他参数是要传入的值。为了获取第一个参数之后的所有参数,在arguments对象上调用数组的slice方法,并传入参数1表示被返回的数组包含从第二个参数开始的所有参数。在内部函数(就是return返回的那个匿名函数)中,创建了innerArgs数组用来存放所有传入的参数(又一次用到了slice)。有了存放来自外部函数和内部函数的参数数组后,就可以使用concat方法将两个数组连接起来组合成finalArgs,然后用apply将finalArgs传递给fn。这个函数并未考虑执行环境,所以apply的第一个参数是null。
于是curry的用法就如下:

function add(num1,num2,num3,num4){
  return num1+num2+num3+num4
}

let curriedAdd = curry(add,4);
curriedAdd(1,2,3);   //10

这样你就让函数从开始的add函数的4个参数,改变到curridAdd的三个参数,你还能更精简:

let curriedAdd = curry(add,1,2,3);
curriedAdd(4)  //10

这样你就把参数省略到了一个,但是呢,1,2,3在这里都是固定的。如果不想固定的话,可以用变量代替即可。
这就是柯里化的一个重要的作用,简化函数,让函数只需要传入一个重要的参数。相比以前动不动就很难懂的参数很多的函数,柯里化的意义还是比较大的。

柯里化与函数绑定

柯里化还常常作为函数绑定的一部分包含在其中,构造出更为复杂的bind()函数。例如:

function bind(fn,context){
  let args = Array.prototype.slice.call(argments,2);
  return function(){
    let innerArgs = Array.prototype.slice.call(arguments);
    let finalArgs = args.concat(innerArgs);
    return fn.apply(context,finalArgs);
  };
}

这跟文章开头我们写的bind()函数有很大不同,结合了柯里化。
这堆curry()函数的主要更改在于传入的参数个数,以及它如何影响代码的结果。curry()仅仅接受一个要包裹的函数作为参数,而bind()同时接受函数和一个object对象。这表示给绑定的函数的参数是从第三个开始而不是第二个。这就要更改slice()的第一处调用。另一处更改是在倒数第三行的将object对象传入apply,这里是context。
当你想使用bind()的时候,它就会返回绑定到给定环境的函数,并且可能它其中某些参数已经被设置好了。当你想除了event对象再额外给时间处理程序传递参数时,这非常有用。例如:

let handler = {
  message:"Event handled",
  handleClick:function(name,event){
    alert(this.message + ":" + name + ":" + event.type);
  }
};
let btn = document.getElementById("my-btn");
EventUntil.addHandler(btn,"click",bind(handler.handleClick,handler,"my-btn"));

上面的代码中,handler.handleClick()方法接受了两个参数:要处理的元素的名字和event对象。作为第三个参数传递给bind()函数的名字,又被传递给了handler.handleClick(),而handler.handleClick()也会同样接受到event对象。

es5中bind()方法的柯里化

EcmaScript5中的bind()方法也实现了函数的柯里化,只要在this的值之后在传入另一个参数即可。

let handler= {
  message:"Event handled",
  handleClick:function(name,event){
    alert(this.message + ":" + name + ":" + event.type);
  }
};
let btn = document.getElementById("my-btn");
EventUntil.addHandler(btn,"click",handler.handlerClick.bind(handler,"my-btn"));

JS中的柯里化函数和绑定函数提供了强大的动态函数创建功能。使用bind()还是curry()要根据是否需要object对象来确定。他们都能用于创建复杂的算法和功能,当然两者都不能滥用。因为每个函数都会带来额外的开销,占用额外的内存。牺牲性能。
我一般喜欢直接用bind();

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,099评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,828评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,540评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,848评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,971评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,132评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,193评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,934评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,376评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,687评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,846评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,537评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,175评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,887评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,134评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,674评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,741评论 2 351

推荐阅读更多精彩内容