柯里化算是特殊的偏函數,把一個多參數函數轉換成多個單參數函數,也就是說把一個具備n個參數的函數轉換成n個一元函數
// 正常寫法 function add (a, b) { return a + b } const resAdd = add(2, 3) console.log(resAdd) // 5 // 柯里化 function currieAdd (a) { return function (b) { return a + b } } const resCurrie = currieAdd(2)(3) console.log(resCurrie) // 5
上面的示例代碼比較簡單,若是有十幾個參數呢?因此須要一個通用柯里化的寫發
代碼的關鍵點在於javascript
- 閉包,調用柯里化函數(currie)返回另一個函數(_myFn),經過閉包緩存真正執行運算的函數(fn)和參數(args)
- 經過返回的函數傳遞參數,並進行判斷,若是參數已經傳遞夠了,就執行函數(fn)並返回結果,若是參數還沒傳遞完,則繼續返回函數(_myFn)接收參數
// 柯里化一個函數 function currie (fn) { // 利用閉包緩存傳遞進來的參數 const args = [] return function _myFn (arg) { args.push(arg) if (args.length === fn.length) { // 說明參數已經傳遞夠了,執行fn函數並返回結果 return fn.apply(null, args) } else { // 發現參數沒有傳遞完,則返回_myfn函數,繼續調用 return _myFn } } } // 示例 1 function add_1 (a, b) { return a + b } const currieAdd_1 = currie(add_1) const res1 = currieAdd_1(2)(3) console.log(res1) // 5 // 示例 2 function add_2 (a, b, c, d, e) { return a + b + c + d + e } const currieAdd_2 = currie(add_2) const res2 = currieAdd_2(1)(2)(3)(4)(5) console.log(res2) // 15
偏函數又叫局部應用,固定函數的一個或多個參數,也就是說把一個n
元函數轉換成一個n - x
元函數
// 封裝一個ajax方法 function ajax (url, data, callback) { ... } // 調用ajax方法, ajax('http://lyn.com', { a: 'aa' }, function () { // 回調 A }) ajax('http://lyn.com', { b: 'bb' }, function () { // 回調 B }) ... ajax('http://lyn.com', { y: 'yy' }, function () { // 回調 Y })
發現以上全部的調用,第一個參數都同樣,這時候就須要想有沒有什麼方法能夠簡化這種重複參數的填寫,
偏函數
出馬
// 偏函數 function partial (url) { return function (data, cb) { ajax(url, data, cb) } } // 調用偏函數 const partialAjax = partial('http://lyn.com') // 發送ajax請求 partialAjax({ a: 'aa' }, function () { // 回調 A }) partialAjax({ b: 'bb' }, function () { // 回調 B }) ... partialAjax({ y: 'yy' }, function () { // 回調 Y })
代碼的關鍵點java
偏函數的代碼比較簡單,就是利用閉包緩存實際的執行方法(fn)和與之的參數(preset),而後返回一個接收剩餘參數的方法,方法的實現就是執行fn並返回結果
function partial (fn, ...preset) { return function (...args) { return fn.apply(null, preset.concat(args)) } } // 示例, 經過一個簡單的add方法來模擬 function add (a, b, c, d) { return a + b + c + d } // 屢次調用傳遞的前兩個參數是同樣的 // add(1, 2, 3, 4) // add(1, 2, 5, 6) const partialAdd = partial(add, 1, 2) const res1 = partialAdd(3, 4) console.log(res1) // 10 const res2 = partialAdd(5, 6) console.log(res2) // 14
柯里化其實就是偏函數的特殊狀況,因此在反柯里化這裏就之說偏函數,我以爲這樣更合適
- 偏函數:偏函數是對高階函數的降階處理,再樸素點的描述就是,下降函數的通用性,建立一個針對性更強的函數,好比上面講的
偏函數
部分的ajax
和partialAjax
- 反柯里化:和偏函數恰好相反,增長方法的適用範圍(即通用性)
Function.prototype.uncurrie = function (obj) { // 參數obj是須要操做的對象 // 這裏的this是指obj對象須要借用的方法,好比示例中的Array.prototype.push const fnObj = this return function (...args) { // 難點,如下代碼至關於:fnObj.call(obj, ...args), 沒理解請看下面的 「代碼解析」 部分 return Function.prototype.call.apply(fnObj, [obj, ...args]) } } // 示例,導出Array.prototype.push方法給對象使用 const obj = { a: 'aa' } const push = Array.prototype.push.uncurrie(obj) push('b') push('c') console.log(obj) // {0: "b", 1: "c", a: "aa", length: 2}
這部份內容負責解析上面的
通用代碼
ajax
- 首先聲明,我的以爲這個通用代碼是不必的,由於這段通用代碼的本質就是
call、apply
,經過call、apply改變方法的this上下文,使得對象可使用不屬於它的方法,這也是反柯里化的本質,加強方法的使用範圍- 這段通用代碼的難點在於
Function.prototype.call.apply(fnObj, [obj, ...args])
這句,如下解析採用通用代碼
中的示例代碼
- 如下解釋須要你熟悉
apply、call
方法的源碼實現,若是不熟悉請參考 javascript源碼解析,裏面的call、apply兩部分的源碼解析會回答你的疑問- 正式開始解析
通用代碼
,經過通用代碼
中的示例
代碼進行講解通用代碼
其實就是個閉包,執行Array.prototype.push.uncurrie(obj)
,傳遞一個須要操做的對象(const obj = {a: 'aa'}),其中fnObj = Array.prototype.push
,這時向外面return一個接收參數的函數- 返回的函數中就一句代碼:
return Function.prototype.call.apply(fnObj, [obj, ...args])
,緩存
- 上面的代碼能夠翻譯爲:
return Function.prototype.call.apply(Array.prototype.push, [{a: 'aa'}, ...args])
閉包
- 再進一步翻譯(須要瞭解call、apply的原理,不明白請參考javascript源碼解析):
return Array.prototype.push.call({a: 'aa'}, ...args)
,這句就等同於:Arrray.prototype.push.call(obj, 'b')
,看到這裏就會明白我開始說的 「聲明」 部分的意思了app
柯里化其實就是特殊的偏函數,偏函數的本質就是經過調用函數,預置一部分參數,而後返回一個參數更少但針對性更強的函數;而反柯里化,不知道爲啥叫反柯里化,感受應該叫反偏函數更好一點,反柯里化做用和偏函數相反,它的本質是加強一個函數的使用範圍,讓一個對象可使用不屬於對象本身的方法,就像apply、call、bind(也有偏函數的做用)的做用,而事實上反柯里化就是經過apply、call方法實現的