最近在學習javascript函數式編程,對其中大名鼎鼎的curry十分感興趣,curry函數能夠接受一個函數,咱們暫且稱之爲原始函數,返回的也是一個函數,柯里化函數,這個返回的柯里化函數功能十分強大,他在執行的過程當中,不斷的返回一個貯存了傳入參數的函數,直到觸發了原始函數執行的條件。這麼說比較歸納,那麼就舉個例子來講明一下:javascript
原始函數:java
var add = (x, y) => x + y
柯里化函數:編程
var curryAdd = curry(add)
這個add須要兩個參數,可是咱們的curryAdd執行能夠傳入更少的參數,當傳入的參數少於add須要的參數的時候,add函數並不會執行,curryAdd就會將這個參數記下來,而且返回另一個函數,這個函數能夠繼續執行傳入參數,咱們會有一個變量專門記錄傳入參數的狀況,若是傳入參數的總數等於add須要參數的總數,咱們就激活了原始參數執行,就會返回咱們想要的結果。app
// 此時只傳入了一個參數 根據判斷返回的是一個函數 var add2 = curryAdd(2) // add2 = function(...) {}
// 此時累計傳入了兩個參數 等於了add須要參數的總和 因此返回的是一個結果 // 至關於執行了add(2)(3) var result = add2(3) // result = 5
仍是很不錯的是吧,好吧,咱們的目的是爲了寫出這個神奇curry函數,並且還要一行寫出來,不要着急,先分析一下怎麼去寫,而後再一步步的優化。函數式編程
那根據上面的描述,咱們看一下curry函數須要什麼,首先須要一個變量,用來存下來原始函數的參數個數,咱們知道function有一個屬性爲length,對就是它,咱們用limit存下來函數
var curry = function(fn) { var limit = fn.length ... }
curry函數要返回一個函數, 這個函數是要執行的,那麼問題就是,咱們要判斷這個函數的執行是否激活了原始函數的執行,問題就出如今傳入的參數上面。返回函數仍是結果?這的確是一個問題,
咱們先寫返回結果的狀況,當傳入的參數等於原始函數須要的參數時,咱們執行原始函數fn學習
var curry = function(fn) { var limit = fn.length return function (...args) { if (args.length >= limit) { return fn.apply(null, args) } } }
不然呢 咱們就要返回一個貯存了參數的函數,這裏有兩點,一是參數的傳入歷史咱們要記錄下來,二是這個返回的函數須要作些什麼優化
var curry = function(fn) { var limit = fn.length return function (...args) { if (args.length >= limit) { return fn.apply(null, args) } else { return function(...args2) { } } } }
看出來了吧 咱們只須要把返回函數執行的參數累加起來就達到了記錄參數傳入狀況的目的,因而咱們想到了concat 對 args.concat(args2), 依次類推,咱們返回的函數要作的就是重複作上面的事情,也就是參數爲args的函數要作的事情,因此他須要一個名字,否則咱們無法執行,咱們叫它judgeCurry
因此正如咱們所說的,要麼返回一個函數,要麼執行原始函數。code
var curry = function(fn) { var limit = fn.length return function judgeCurry (...args) { if (args.length >= limit) { return fn.apply(null, args) } else { return function(...args2) { return judgeCurry.apply(null, args.concat(args2)) } } } }
咱們終於寫完了這個神奇的curry函數,它真的很強大,配合compose,那真是一個字,爽。
咱們的目的仍是一行把上面那個函數寫出來,一行寫?怎麼寫?對了,用ES6啊,因而一番折騰ip
var currySingle = fn => judgeCurry = (...args) => args.length >= fn.length ? fn.apply(null, args) : (...args2) => judgeCurry.apply(null, args.concat(args2))
好,咱們看一下哪有問題,對了,就是咱們爲了避免用limit參數,用了就得賦值,賦值就不能一行搞定了,就會變成這樣
var currySingle = fn => { var limit = fn.length var judgeCurry = null return judgeCurry = (...args) => args.length >= limit ? fn.apply(null, args) : (...args2) => judgeCurry.apply(null, args.concat(args2)) }
須要判斷參數的時候不斷的對fn.length求值,可是fn.length的值是肯定的,咱們不想每次都求值,但又不想用limit怎麼辦,有什麼辦法呢?你必定想到了,當即執行函數!!
var currySingle = fn => ((limit) => judgeCurry = (...args) => args.length >= limit ? fn.apply(null, args) : (...args2) => judgeCurry.apply(null, args.concat(args2)))(fn.length)
不得不感嘆javascript的神奇,終於,咱們就一行將這個神奇的curry寫出來了。。。