JS中的反柯里化( uncurrying)

来源:http://www.cnblogs.com/moqiutao/archive/2017/08/17/7384525.html
-Advertisement-
Play Games

反柯里化 相反,反柯里化的作用在與擴大函數的適用性,使本來作為特定對象所擁有的功能的函數可以被任意對象所用.即把如下給定的函數簽名, 轉化成一個函數形式,簽名如下: 這就是 反柯里化的形式化描述。 例如,下麵的一個簡單實現: 解釋: uncurrying是定義在Function的prototype上 ...


反柯里化

相反,反柯里化的作用在與擴大函數的適用性,使本來作為特定對象所擁有的功能的函數可以被任意對象所用.
即把如下給定的函數簽名,

obj.func(arg1, arg2)

轉化成一個函數形式,簽名如下:

func(obj, arg1, arg2)

這就是 反柯里化的形式化描述。

例如,下麵的一個簡單實現:

Function.prototype.uncurrying = function() {
    var that = this;
    return function() {
        return Function.prototype.call.apply(that, arguments);
    }
};

function sayHi () {
    return "Hello " + this.value +" "+[].slice.call(arguments);
}
var sayHiuncurrying=sayHi.uncurrying();
console.log(sayHiuncurrying({value:'world'},"hahaha"));

解釋:

  • uncurrying是定義在Function的prototype上的方法,因此對所有的函數都可以使用此方法。調用時候:sayHiuncurrying=sayHi.uncurrying(),所以uncurrying中的 this 指向的是 sayHi 函數; (一般原型方法中的 this 不是指向原型對象prototype,而是指向調用對象,在這裡調用對象是另一個函數,在javascript中函數也是對象)
  • call.apply(that, arguments) 把 that 設置為 call 方法的上下文,然後將 arguments 傳給 call方法,前文的例子,that 實際指向 sayHi,所以調用 sayHiuncurrying(arg1, arg2, ...) 相當於 sayHi.call(arg1, arg2, ...);
  • sayHi.call(arg1, arg2, ...), call 函數把 arg1 當做 sayHi的上下文,然後把 arg2,... 等剩下的參數傳給sayHi,因此最後相當於 arg1.sayHi(arg2,...);
  • 因此,這相當於 sayHiuncurrying(obj,args) 等於 obj.sayHi(args)。

最後,我們反過來看,其實反柯里化相當於把原來 sayHi(args) 的形式,轉換成了 sayHiuncurrying(obj,args),使得sayHi的使用範圍泛化了。 更抽象地表達, uncurryinging反柯里化,使得原來 x.y(z) 調用,可以轉成 y(x',z) 形式的調用 。 假設x' 為x或者其他對象,這就擴大了函數的使用範圍。

通用反柯里化函數

上面例子中把uncurrying寫進了prototype,這不太好,我們其實可以把 uncurrying 單獨封裝成一個函數;

var uncurrying= function (fn) {
    return function () {
        var args=[].slice.call(arguments,1);
        return fn.apply(arguments[0],args);        
    }    
};

上面這個函數很清晰直接。
使用時 調用 uncurrying 並傳入一個現有函數 fn, 反柯里化函數會返回一個新函數,該新函數接受的第一個實參將綁定為 fn 中 this的上下文,其他參數將傳遞給 fn 作為參數。

所以,對反柯里化更通俗的解釋可以是 函數的借用,是函數能夠接受處理其他對象,通過借用泛化、擴大了函數的使用範圍。

所以 uncurrying更常見的用法是對 Javascript 內置的其他方法的 借調 而不用自己都去實現一遍。

文字描述比較繞,還是繼續看代碼:

var test="a,b,c";
console.log(test.split(","));

var split=uncurrying(String.prototype.split);   //[ 'a', 'b', 'c' ]
console.log(split(test,','));                   //[ 'a', 'b', 'c' ]

split=uncurrying(String.prototype.split) 給 uncurrying 傳入一個具體的fn,即String.prototype.split ,split 函數就具有了 String.prototype.split 的功能,函數調用 split(test,',') 時,傳入的第一個參數為 split 執行的上下文,剩下的參數相當於傳給原 String.prototype.split 函數。

再看一個例子:

var $ = {};
console.log($.push);                          // undefined
var pushUncurrying = uncurrying(Array.prototype.push);
$.push = function (obj) {
    pushUncurrying(this,obj);
};
$.push('first');
console.log($.length);                        // 1
console.log($[0]);                            // first
console.log($.hasOwnProperty('length'));      // true

這裡模仿了一個“類似jquery庫” 實現時借用 Array 的 push 方法。 我們知道對象是沒有 push 方法的,所以 console.log(obj.push) 返回 undefined,可以借用Array 來處理 push,由原生的數組方法(js引擎)來維護 偽數組對象的 length 屬性和數組成員。

同樣的道理,我們還可以繼續有:

var indexof=uncurrying(Array.prototype.indexOf);
$.indexOf = function (obj) {
    return indexof(this,obj);
};
$.push("second");
console.log($.indexOf('first'));              // 0
console.log($.indexOf('second'));             // 1
console.log($.indexOf('third'));              // -1

例如我們在實現自己的類庫時,有些方法如果有些方法和原生的類似,那麼可以通過 uncurrying 借用原生方法。

我們還可以把 Function.prototype.call/apply 方法 uncurring,例如:

var call= uncurrying(Function.prototype.call);
var fn= function (str) {
    console.log(this.value+str);
};
var obj={value:"Foo "};
call(fn, obj,"Bar!");                       // Foo Bar!

這樣可以非常靈活地把函數也當做一個普通“數據”來使用,有函數式編程的趕腳,在一些類庫中經常能看到這樣的用法。

通用 uncurrying 函數的進擊

上面的 uncurrying 函數是比較符合思維習慣容易理解的版本,接下來一路進擊,看幾個其他版本:

首先,如果B格高一點,uncurrying 也可能寫成這樣:

var uncurrying= function (fn) {
    return function () {
        var context=[].shift.call(arguments);
        return fn.apply(context,arguments);
    }
};

當然如果還需要再提升B格,那麼還可以是這樣:

var uncurrying= function (fn) {
    return function () {        
        return Function.prototype.call.apply(fn,arguments);
    }
};

參考地址

淺析 JavaScript 中的 函數 uncurrying 反柯里化


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一springmvc項目中我新增記錄完全ok,編輯就是不行,後臺方法進不去。老是報錯HTTP ERROR 400 Bad Request。 經過查詢,說是400表示請求中的語法錯誤。 我把新增記錄的請求信息拷貝下來,把編輯的請求信息也拷貝下來,然後用notepad++比較兩個請求,終於發現原因就是i ...
  • 第一種原因: no Session 錯誤 dao層中get方法換成了load方法,或者其他原因引起. 原因分析: 真正用到代理對象的時候,代理對象沒有值,並且session的生命周期已經走完了. 解決方案:1,load()換成get(),或者立即查詢,比如列印一下. 2,延長session的存活時間 ...
  • spl_autoload_register() 函數可以註冊任意數量的自動載入器,當使用尚未被定義的類(class)和介面(interface)時自動去載入。通過註冊自動載入器,腳本引擎在 PHP 出錯失敗前有了最後一個機會載入所需的類。_autoload()自動載入類:當我們實例化一個未定義的類時... ...
  • 1.工廠模式,在各種BeanFactory以及ApplicationContext創建中都用到了 2.模版模式,在各種BeanFactory以及ApplicationContext實現中也都用到了 3.代理模式,Spring AOP 利用了 AspectJ AOP實現的! AspectJ AOP 的 ...
  • 在開發django應用的過程中,使用開發者模式啟動服務是特別方便的一件事,只需要 python manage.py runserver 就可以運行服務,並且提供了非常人性化的autoreload機制,不需要手動重啟程式就可以修改代碼並看到反饋。剛接觸的時候覺得這個功能比較人性化,也沒覺得是什麼特別高 ...
  • 引言: 最近的一個項目,由於資料庫表巨多,導致需要創建N多個java實體、dao、mapper.xml映射文件,如果均使用純手工編寫,無疑需要耗費大量時間和精力。於是上網學習了mybatis generator的使用。 現在項目寫完了,閑暇之餘把乾貨奉上,供大家直接使用。 需求場景: 當你的java ...
  • [1]引入 [2]過渡組件 [3]過渡類名 [4]transition [5]animation [6]自定義類名 [7]過渡時間 ...
  • 在html5之前,瀏覽器要實現數據的存儲,一般都是用cookie,但是cookie有功能變數名稱和大小限定. html5流行之後,可以通過localStorage和sessionStorage實現瀏覽器端的數據存儲,這兩者有什麼特點呢? sessionStorage sessionStorage屬於臨時會話 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...