javascript之反柯裏化(uncurrying)
阿新 • • 發佈:2018-02-15
ons curry -a int 復制 style 就是 引擎 pre
在JavaScript中,當我們調用對象的某個方法時,其實不用去關心該對象原本是否被設計為擁有這個方法,這是動態類型語言的特點。可以通過反柯裏化(uncurrying)函數實現,讓一個對象去借用一個原本不屬於他的方法。
通常讓對象去借用一個原本不屬於它的方法,可以用call和apply實現,如下
更常見的場景之一是讓類數組對象去借用Array.prototype的方法;
(function(){ Array.prototype.push.call(arguments,4) console.log(arguments);//[1, 2, 3, 4] })(1,2,3)
擴展:為什麽類數組對象能夠借用數組的方法呢?不妨理解下V8的引擎源碼,就以Array.prototype.push為例:
function ArrayPush() { CHECK_OBJECT_COERCIBLE(this, "Array.prototype.push"); var array = TO_OBJECT(this); var n = TO_LENGTH_OR_UINT32(array.length); var m = %_ArgumentsLength(); ....... for (var i = 0; i < m; i++) { array[i+n] = %_Arguments(i); } var new_length = n + m; array.length= new_length; return new_length; }
通過這段代碼大致可以看出,Array.prototype.push實際上是一個屬性復制的過程,把參數按照下標依次添加到被push的對象上面,順便修改了這個對象的length屬性,這個對象到底是數組還是類數組並不重要。從源碼可以看出,只要對象本身可以存取屬性,且length屬性可讀寫,就可以借用Array原型的push方法。
這樣一來,方法中用到this的地方,就不在局限原本的對象,而是加以泛化並得到更廣的適用性。那麽有沒有辦法把泛化this的過程提取出來呢?那麽反柯裏化(uncurrying)就是解決這個問題的。反科裏化(uncurrying)的話題來自JavaScript之父Brendan Eich在2011年發表的一篇文章,以下代碼是實現方式之一:
Function.prototype.uncurrying = function() { var self = this; return function() { var obj = Array.prototype.shift.call(arguments); return self.apply(obj, arguments); }; };
然後就可以定義一個push函數,更加簡潔和明了的實現了一個不在局限於數組的push方法。如下:
var push = Array.prototype.push.uncurrying(); (function(){ push(arguments,4); console.log(arguments);//[1,2,3,4] })(1,2,3)
除了剛剛的一種反柯裏化實現,還有另一種實現方式:
Function.prototype.uncurrying = function() { var self = this; return function() { return Function.prototype.call.apply(self,arguments) }; }
參考書籍:javascript設計模式與開發實踐
javascript之反柯裏化(uncurrying)