JS中的反柯里化

做爲函數式編程語言,JS帶來了不少語言上的有趣特性,好比柯里化和反柯里化。html

能夠對照另一篇介紹 JS 柯里化 的文章一塊兒看~前端

1. 簡介

柯里化,是固定部分參數,返回一個接受剩餘參數的函數,也稱爲部分計算函數,目的是爲了縮小適用範圍,建立一個針對性更強的函數。核心思想是把多參數傳入的函數拆成單參數(或部分)函數,內部再返回調用下一個單參數(或部分)函數,依次處理剩餘的參數。編程

反柯里化,從字面講,意義和用法跟函數柯里化相比正好相反,擴大適用範圍,建立一個應用範圍更廣的函數。使原本只有特定對象才適用的方法,擴展到更多的對象。segmentfault

2. 實現

先來看看反柯里化的通用實現吧~數組

Function.prototype.unCurrying = function() {
  const self = this
  return function(...rest) {
    return Function.prototype.call.apply(self, rest)
  }
}

解釋下:微信

  1. 爲Function原型添加uncurrying方法,並在執行的時候保存執行unCurrying的方法到self
  2. 借用apply把要借用的函數做爲this環境賦給call,並傳入以後的形參做爲參數執行

還有一個實現:app

Function.prototype.unCurrying = function() {
  return this.call.bind(this)
}

若是你以爲把函數放在Function.prototype上不太好,也能夠這樣:編程語言

function unCurrying(fn) {
  return function(tar, ...argu) {
    return fn.apply(tar, argu)
  }
}

3. 使用

3.1 簡單使用

unCurrying通用實現簡單的實用一下試試:函數式編程

Function.prototype.unCurrying = function() {
  const self = this                        // 這裏的self就是Array.prototype.push方法
  return function(...rest) {              // rest爲傳入的兩層參數[[1,2,3],4]
    return Function.prototype.call.apply(self, rest)
  }
}
const push = Array.prototype.push.unCurrying()

~function(...rest) {       // rest:[1,2,3]
  push(rest, 4)
  console.log(rest)    // [1, 2, 3, 4]
}(1, 2, 3)

3.2 借用其餘方法

反柯里化其實反映的是一種思想,即擴大方法的適用範圍,仍然調用剛剛的通用unCurrying方法借用push方法:函數

const push = Array.prototype.push.unCurrying()

const obj = { a: '嘻嘻' }
push(obj, '呵呵', '哈哈', '嘿嘿')
console.log(obj)                    // { '0': '呵呵', '1': '哈哈', '2': '嘿嘿', a: '嘻嘻', length: 3 }

至關於obj.push(...),obj不只多了相似於數組同樣以數字做爲索引的屬性,還多了個相似於數組的length屬性,讓引擎自動管理數組成員和length屬性;(文後有V8引擎實現push方法的源碼)
這樣一個數組的push方法就被借用出來,能夠應用於任何其餘對象了。

只要是方法,unCurrying就能夠借用,call方法也能夠:

var call = Function.prototype.call.unCurrying();
function $(id) {
    return this.getElementById(id);
}
call($, document, 'demo')            // #demo 元素

至關於document.$('demo'),成功的借用了call方法,固然能夠把document改爲你但願做爲this綁定到$的任何對象,好比{ getElementById:T=>console.log(T+'呃') } // demo呃

3.3 借用本身

unCurrying自己也是方法,也能夠借用本身...-。-

const unCurrying = Function.prototype.unCurrying.unCurrying()
const map = unCurrying(Array.prototype.map)
map({ 0: 4, 1: 'a', 2: null, length: 3 }, n => n + n)                    // [8, "aa", 0]

神奇吧~

4. 總結

簡單說,函數柯里化就是對高階函數的降階處理,縮小適用範圍,建立一個針對性更強的函數。舉栗子:

function(arg1,arg2)        // => function(arg1)(arg2)
function(arg1,arg2,arg3)        // => function(arg1)(arg2)(arg3)
function(arg1,arg2,arg3,arg4)        // => function(arg1)(arg2)(arg3)(arg4)
function(arg1,arg2,…,argn)        // => function(arg1)(arg2)…(argn)

而反柯里化就是反過來,增長適用範圍,讓方法使用場景更大。使用unCurrying, 能夠把原生方法借出來,讓任何對象擁有原生對象的方法。舉個栗子:

obj.func(arg1, arg2)        // => func(obj, arg1, arg2)

也能夠這樣理解:
柯里化是在運算前提早傳參,能夠傳遞多個參數;
反柯里化是延遲傳參,在運算時把原來已經固定的參數或者this上下文等看成參數延遲到將來傳遞。


附:

V8引擎中Array.prototype.push方法源碼實現:

function ArrayPush() {
    var n = TO_UINT32(this.length);
    var m = %_ArgumentsLength();
    for (var i = 0; i < m; i++) {
        this[i + n] = %_Arguments(i);        // 屬性拷貝
        this.length = n + m;                    // 修正length
        return this.length;
    }
}

網上的帖子大多深淺不一,甚至有些先後矛盾,在下的文章都是學習過程當中的總結,若是發現錯誤,歡迎留言指出~

參考:
JS 柯里化
前端開發者進階之函數反柯里化unCurrying
JavaScript中有趣的反柯里化
js柯里化適用場景,優缺點分別是什麼,還有個反柯里化?
JS進階篇--JS中的反柯里化( uncurrying)

PS:歡迎你們關注個人公衆號【前端下午茶】,一塊兒加油吧~

另外能夠加入「前端下午茶交流羣」微信羣,長按識別下面二維碼便可加我好友,備註加羣,我拉你入羣~

相關文章
相關標籤/搜索