维基百科中的解释是:柯里化是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,而且返回接受余下的参数并且返回结果的新函数的技术。意思就是当函数被调用时,返回的函数还须要设置一些传入的参数。app
首先来看一个简单的例子,有下面一个函数:函数
function add(num1, num2) { return num1 + num2; }
咱们把它改写成下面这样:性能
var fn = function(a) { return function (b) { return a + b; } }
能够这样调用函数:fn(2)(3)。上面使用了匿名函数来实现多参数函数的方法,虽然这并非柯里化的函数,但能够帮助咱们理解柯里化的含义。this
咱们能够在内置构造函数Function()的原型上来添加一个柯里化函数,这样全部的函数均可以调用。下面是通用柯里化函数的实现:prototype
Function.prototype.currying = function () { var that = this; var args = [].slice.call(arguments); return function () { return that.apply(null, args.concat([].slice.call(arguments))); } }
如今用柯里化函数将上面的add
函数柯里化:code
var curriedAdd = add.currying(2); curriedAdd(3); // 5
也能够一次性传入两个参数:对象
var curriedAdd = add.currying(2, 3); curriedAdd(); // 5
咱们知道在原生对象的原型上扩展方法是不太好的,由于可能会致使命名冲突。因此最好不要把currying函数扩展在Function的原型上,下面是改写的currying函数:原型
function currying(fn) { var args = [].slice.call(arguments, 1); return function () { return that.apply(null, args.concat([].slice.call(arguments))); } }
改写以后currying
函数的第一个参数是要被柯里化的函数,能够这样调用:it
var curriedAdd = currying(add, 2); curriedAdd(3); // 5 或 var curriedAdd = currying(add, 2, 3); curriedAdd(); // 5
上面的add
函数只是两个数字的相加,若是咱们须要n个数字相加,上面的currying函数已经不能知足要求了,下面是修改后的currying函数:io
function currying(fn) { var argsArr = []; return function () { if (arguments.length === 0) { return fn.apply(null, argsArr); } else { [].push.apply(argsArr, arguments); } } }
多个数字相加:
var add = function () { var num = 0; [].forEach.call(arguments, function (item, i) { num += item; }) return num; } var curriedAdd = currying(add); curriedAdd(2); curriedAdd(3); curriedAdd(4); curriedAdd(5); curriedAdd(); // 14
这样作有什么好处呢?假如说咱们只想知道这个月花了多少钱,而中间的某一天以前花了多少咱们并不想知道,咱们只在意结果,不在意过程,上面的currying函数很好地解决了这个问题。有的人说这样作能够节省性能,我倒以为这和性能没多大关系,或者说这样作的目的并非为了性能,由于每次计算结果和最后一块儿计算结果是同样的,都是要计算同样的次数。还有一个好处就是能够复用currying函数,好比咱们要多个数字相乘或者其余操做,均可以用currying函数,处理数字只需修改fn
参数就能够。
说到柯里化就不得不说Function.prototype.bind
这个方法了,它也实现了函数的柯里化。咱们能够本身来实现一个bind
函数:
function bind(fn, context) { var args = [].slice.call(arguments, 2); return function () { return fn.apply(context, args.concat([].slice.call(arguments))); } }
假如咱们须要改变fn中的this上下文,就能够用bind函数,不然能够用currying函数。