首頁 >web前端 >js教程 >JS中的反柯里化

JS中的反柯里化

大家讲道理
大家讲道理原創
2017-08-19 10:33:481706瀏覽

反柯里化

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


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);                          // undefinedvar pushUncurrying = uncurrying(Array.prototype.push);
$.push = function (obj) {
    pushUncurrying(this,obj);
};
$.push('first');
console.log($.length);                        // 1console.log($[0]);                            // firstconsole.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'));              // 0console.log($.indexOf('second'));             // 1console.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);
    }
};

以上是JS中的反柯里化的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn