1. 程式人生 > >javascript之反柯裏化(uncurrying)

javascript之反柯裏化(uncurrying)

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)