本文主要講了JavaScript中科里化和反科里化this的方法.主題來自於Brendan Eich(JavaScript之父)的一個tweet.
1.反科里化(Uncurrying)this
反科里化this的意思是:把一個簽名如下的方法:
obj.foo(arg1, arg2)轉換成另外一個簽章如下的函數:
foo(obj, arg1, arg2)想要知道這麼做有什麼用,我們首先得了解一下通用方法.
2.通用方法( Generic methods)
通常情況下,某個特定的方法只能在某種特定類型的物件實例上使用.但是,有一些方法如果還可以使用在其他類型的物件實例上的話,那會很有用,例如:
this可以看做是forEach()方法的隱含參數.滿足下面這三條規則的物件都可以呼叫forEach()方法,都可以作為這個隱含的this:
•具有length屬性: this.length
•能夠透過索引存取物件元素: this[i]
•能夠檢查屬性的存在性: i in this
arguments物件(包含了一次函數調用的所有實參)不是一個Array實例,所以它不能直接調用forEach()方法.但是你它滿足調用forEach方法的三個條件.為了讓該對象能夠調用到forEach()方法,我們只需要讓隱含的this參數作為顯式參數.幸運的是,每個函數都有call()方法讓我們來做件事:
3.反科里化this的幾個用途
用例1:透過map()呼叫一個方法. Array.prototype.map()方法允許你在一個陣列中的每個元素上呼叫一個函數.但如果你想呼叫的不是函數還是方法呢?可以利用反科里化this這麼做:
Array.map
4.實作uncurryThis() <br><br>以下是實作uncurryThis方法的三種方式. <strong><br>實作1: Brendan Eich寫的</strong><br><br><br><div class="codetitle">
<span>複製程式碼<a style="CURSOR: pointer" data="60855" class="copybut" id="copybut60855" onclick="doCopy('code60855')"><u></u> 程式碼如下:</a></span> </div>Function.prototype.uncurryThis = function () { <div class="codebody" id="code60855">var f = thisturn <br> function () { <br>var a = arguments; <br>return f.apply(a[0], [].slice.call(a, 1)); <br>}; <br>}; <br><br><br>實作2: 呼叫反科里化過的函數相當於在原方法上透過呼叫它的call()方法來執行.我們可以透過bind()方法把這個call()方法借過來: <br><div class="codetitle">
<span><a style="CURSOR: pointer" data="87558" class="copybut" id="copybut87558" onclick="doCopy('code87558')"><u>複製程式碼</u></a></span> 程式碼如下:</div>
<div class="codebody" id="code87558"> <br>Function.prototype.uncurryThis = function (> <br>Function.prototype.uncurryThis = function (return <s. call.bind>}; <br></s.>
</div> <br>實作3: 定義的標準方法最好不要依賴過多的外部方法.此外,bind()方法只在ECMAScript 5中可用.因此我們重寫了上面的實現2,如下: <br><br><div class="codetitle">
<span><a style="CURSOR: pointer" data="7449" class="copybut" id="copybut7449" onclick="doCopy('code7449')"><u>複製代碼</u></a></span> 代碼如下:</div>
<div class="codebody" id="code7449"> <br>Function.prototype.uncurryThis = function () { <br>var f = this; <br>return function () { <br>return f.call.apply(f, arguments) <br>}; 🎜>}; <br><br> </div>上面的程式碼仍然是隱式的借用了call()方法. <br><br><br>5.反向操作也很有用– 科里化this <strong><br>uncurryThis()的反向操作稱為curryThis().它將原函數的第一個參數轉換成隱含的this參數.假如有個原函數: </strong><br><div class="codetitle"><span><a style="CURSOR: pointer" data="74336" class="copybut" id="copybut74336" onclick="doCopy('code74336')">複製程式碼<u></u></a> 程式碼如下:</span></div> <div class="codebody" id="code74336">function(self, arg) { <br>return self.foo arg <br>; } <br><br> </div>科里化this後成為: <br><br><div class="codetitle">
<span><a style="CURSOR: pointer" data="73673" class="copybut" id="copybut73673" onclick="doCopy('code73673')">複製程式碼<u></u></a> 程式碼🎜> </span>function(arg) { </div>return this.foo arg; <div class="codebody" id="code73673">} <br><br> <br>用例: 讓一個方法把自己的this值傳遞到一個內嵌函數裡.原來的寫法: <br>
</div>
<br><br><div class="codetitle">複製代碼<span><a style="CURSOR: pointer" data="49106" class="copybut" id="copybut49106" onclick="doCopy('code49106')"><u> 代碼如下:</u></a></span>var obj = {</div>
<div class="codebody" id="code49106">
<br>var obj = { var self = this; // 讓巢狀的函數存取this<br> 🎜> });<br> },<br> otherMethod: function (arg) { ... }<br>}<br><br><br>科里化後你可以這麼寫:<br>
</div> <br><br><div class="codetitle">複製程式碼<span><a style="CURSOR: pointer" data="22807" class="copybut" id="copybut22807" onclick="doCopy('code22807')"><u> 程式碼如下:</u></a></span>var obj = {</div> method: function (self, arg附加參數`self`<div class="codebody" id="code22807"> someFunction(..., function() {<br> // 傳入附加參數<br> otherMethod: function (arg) { ... }<br>}<br><br><br>我們把隱含的參數this轉換成了顯式的參數self.換句話說:我們把一個動態的this轉換成了一個靜態的變數self.如果this總是作為一個明確的參數,則JavaScript會變的更簡單點.<br>
<br>實作curryThis():<br>
</div>
<br><p>複製程式碼<br></p>
<div class="codetitle">
<span> 程式碼如下:<a style="CURSOR: pointer" data="75145" class="copybut" id="copybut75145" onclick="doCopy('code75145')"><u></u><unction.prototype. currythis="function"> var f = this;</unction.prototype.></a></span> return function () {</div> var a = Array.prototype.slice.call(arguments);<div class="codebody" id="code75145"> a.unshift(this);<br> return f.apply(null, a);<br> };<br>};<br><br><br>
<br><br>6.如果你不想擴充函數原型<br>
</div>
上面實現的方法都是加在了內置構造函數Function()的原型上.你應該可以輕鬆的將它們重寫為獨立的函數.<p><strong></strong></p>
<p>複製程式碼<br></p>
<div class="codetitle"><span> 程式碼如下:<a style="CURSOR: pointer" data="18130" class="copybut" id="copybut18130" onclick="doCopy('code18130')"><div class="codebody" id="code18130">
<br>function uncurryThis(f) {<br> return function () {<br> return f.call.apply(f, arguments) ) {<br> return function () {<br> var a = Array.prototype.slice.call(arguments);<br> , a); <br> };<br>}<br><br><br>
<br><br>7.在uncurryThis()安全的使用在已經存在的不信任的程式碼中</div>
<p>Mark Miller<strong>把uncurryThis()作為例子講解了</strong>「</p>安全的元程式設計<p>」:<tt></tt>譯者註:第一個參數轉換成方法中的this.反科里化this就是把方法中的this轉換成函數的第一個參數.<a href="http://wiki.ecmascript.org/doku.php?id=conventions:safe_meta_programming" target="_blank"></a></p></a></span></div>
</div>