JavaScript 反科裡化 this [譯]

來源:互聯網
上載者:User

本文主要講了JavaScript中科裡化和反科裡化this的方法.話題來自於Brendan Eich(JavaScript之父)的一個tweet.

1.反科裡化(Uncurrying)this

反科裡化this的意思是:把一個簽名如下的方法:

obj.foo(arg1, arg2)轉換成另外一個簽名如下的函數:

foo(obj, arg1, arg2)想要知道這麼做有什麼用,我們首先得瞭解一下通用方法.

2.通用方法(Generic methods)

通常情況下,某個特定的方法只能在某種特定類型的對象執行個體上使用.但是,有一些方法如果還可以使用在其他類型的對象執行個體上的話,那會非常有用,例如: 複製代碼 代碼如下:// 實際實現的簡化版本:
Array.prototype.forEach = function (callback) {
for(var i=0; i<this.length; i++) {
if (i in this) {
callback(this[i], i);
}
}
};

this可以看做是forEach()方法的隱含參數.滿足下面這三條規則的對象都可以調用forEach()方法,都可以作為這個隱含的this:

•具有length屬性: this.length
•能夠通過索引訪問對象元素: this[i]
•能夠檢查屬性的存在性: i in this
arguments對象(包含了一次函數調用的所有實參)不是一個Array執行個體,所以它不能直接調用forEach()方法.但是你它滿足調用forEach方法的三個條件.為了讓該對象能夠調用到forEach()方法,我們只需要讓隱含的this參數作為顯式參數.幸運的是,每個函數都有call()方法讓我們來做件事:

複製代碼 代碼如下:function printArgs() {
Array.prototype.forEach.call(arguments, function (elem, index) {
console.log(index+". "+elem);
});
}

forEach.call()比forEach()方法多一個參數:它的第一個參數就是指定的this值:複製代碼 代碼如下:> printArgs("a", "b")
0. a
1. b

JavaScript中有幾個類似的通用方法都可以以這種方式來調用,這些方法大部分來自Array.prototype.

3.反科裡化this的幾個用途

用例1:通過map()調用一個方法. Array.prototype.map()方法允許你在一個數組中的每個元素上調用一個函數.但如果你想調用的不是函數還是方法呢?可以利用反科裡化this這麼做:

複製代碼 代碼如下:> var toUpperCase = String.prototype.toUpperCase.uncurryThis();
> [ "foo", "bar", "baz" ].map(toUpperCase)
[ 'FOO', 'BAR', 'BAZ' ]

用例2:將一個通用方法轉換成函數. 利用反科裡化this可以將一個方法轉換成一個用法更簡單的函數.比如:複製代碼 代碼如下:Array.forEach = Array.prototype.forEach.uncurryThis();
function printArgs() {
Array.forEach(arguments, function (elem, index) {
console.log(index+". "+elem);
});
}

在未來版本的ECMAScript規範建議中已經有了很多類似的數組方法.
譯者注:Firefox已經實現了Array.map,Array.forEach等方法.

4.實現uncurryThis()
下面是實現uncurryThis方法的三種方式.

實現1: Brendan Eich寫的 複製代碼 代碼如下:Function.prototype.uncurryThis = function () {
var f = this;
return function () {
var a = arguments;
return f.apply(a[0], [].slice.call(a, 1));
};
};

實現2: 調用反科裡化過的函數相當於在原方法上通過調用它的call()方法來執行.我們可以通過bind()方法把這個call()方法借過來: 複製代碼 代碼如下:Function.prototype.uncurryThis = function () {
return this.call.bind(this);
};

實現3: 定義的標準方法最好不要依賴過多的外部方法.此外,bind()方法只在ECMAScript 5中可用.因此我們重寫了上面的實現2,如下:

複製代碼 代碼如下:Function.prototype.uncurryThis = function () {
var f = this;
return function () {
return f.call.apply(f, arguments)
};
};

上面的代碼仍然是隱式的借用了call()方法.

5.反向操作也很有用 – 科裡化this
uncurryThis()的反向操作稱之為curryThis().它將原函數的第一個參數轉換成隱含的this參數.假如有個原函數: 複製代碼 代碼如下:function(self, arg) {
return self.foo + arg;
}

科裡化this後成為: 複製代碼 代碼如下:function(arg) {
return this.foo + arg;
}

用例: 讓一個方法把自己的this值傳遞到一個內嵌函數裡.原來的寫法: 複製代碼 代碼如下:var obj = {
method: function (arg) {
var self = this; // 讓嵌套的函數訪問到this
someFunction(..., function() {
self.otherMethod(arg);
});
},
otherMethod: function (arg) { ... }
}

科裡化後你可以這麼寫:複製代碼 代碼如下:var obj = {
method: function (self, arg) { // 附加參數`self`
someFunction(..., function() {
self.otherMethod(arg);
});
}.curryThis(), // 傳入附加參數
otherMethod: function (arg) { ... }
}

我們把隱含的參數this轉換成了顯式的參數self.換句話說:我們把一個動態this轉換成了一個靜態變數self.如果this總是作為一個顯式的參數,則JavaScript會變的更簡單點.

實現curryThis():

複製代碼 代碼如下:Function.prototype.curryThis = function () {
var f = this;
return function () {
var a = Array.prototype.slice.call(arguments);
a.unshift(this);
return f.apply(null, a);
};
};

6.如果你不想擴充函數原型

上面實現的方法都是加在了內建建構函式Function()的原型上.你應該可以輕鬆的將它們重寫為獨立的函數.

複製代碼 代碼如下:function uncurryThis(f) {
return function () {
return f.call.apply(f, arguments)
};
}
function curryThis(f) {
return function () {
var a = Array.prototype.slice.call(arguments);
a.unshift(this);
return f.apply(null, a);
};
}

7.在uncurryThis()安全的使用在已經存在的不信任的代碼中

Mark Miller把uncurryThis()作為例子講解了“安全的元編程”:

譯者注:科裡化this就是把函數的第一個參數轉換成方法中的this.反科裡化this就是把方法中的this轉換成函數的第一個參數.

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.