維基百科中的解釋是:柯里化是把接受多個參數的函數變換成接受一個單一參數(最初函數的第一個參數)的函數,而且返回接受餘下的參數並且返回結果的新函數的技術。意思就是當函數被調用時,返回的函數還須要設置一些傳入的參數。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函數。