欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

javascript中有趣的反柯里化深入分析

 更新時間:2012年12月05日 10:37:46   投稿:whsnow  
國內(nèi)對前端的研究在某些方面也不遜色于國外,反科里化的話題來自javascript之父Brendan Eich去年的一段twitter,需要深入理解的朋友可以參考本文

寫在前面的話:國內(nèi)對前端的研究在某些方面也不遜色于國外,這篇文章雖然看不太懂,但我很欣賞這種深入研究的精神!

反科里化的話題來自javascript之父Brendan Eich去年的一段twitter. 近幾天研究了一下,覺得這個東東非常有意思,分享一下。先忘記它的名字,看下它能做什么.

不要小看這個功能,試想下,我們在寫一個庫的時候,時常會寫這樣的代碼,拿webQQ的Jx庫舉例。


我們想要的,其實只是借用Array原型鏈上的一些函數(shù)。并沒有必要去顯式的構(gòu)造一個新的函數(shù)來改變它們的參數(shù)并且重新運算。

如果用uncurrying的方式顯然更加優(yōu)雅和美妙,就像這樣:

還能做很多有趣和方便的事情.

甚至還能把call和apply方法都uncurrying,把函數(shù)也當作普通數(shù)據(jù)來使用. 使得javascript中的函數(shù)調(diào)用方式更像它的前生scheme, 當函數(shù)名本身是個變量的時候, 這種調(diào)用方法特別方便.

scheme里面調(diào)用函數(shù)是這樣:

javascript里可以寫的很接近.

再看看jquery庫,由于jquery對象( 即通過$()創(chuàng)建的對象 )是一個對象冒充的偽數(shù)組,它有l(wèi)ength屬性,并且能夠通過下標查找對應的元素,當需要給jquery對象添加一個成員時, 偽代碼大概是:

如果用uncurrying的話, 就可以

借用了array對象的push函數(shù), 讓引擎去自動管理數(shù)組成員和length屬性.

而且可以一次把需要的函數(shù)全部借過來, 一勞永逸. 一段測試代碼:

總的來說, 使用uncurrying技術(shù), 可以讓任何對象擁有原生對象的方法. 好了,如果到這里依然沒有引起你的興趣,那么你可以去干點別的了。

接下來一步一步來看看原理以及實現(xiàn)。
在了解反currying化這個奇怪的名字之前,我們得先搞清楚currying。

通俗點講,currying有點類似買房子時分期付款的方式,先給一部分首付( 一部分參數(shù) ), 返回一個存折( 返回一個函數(shù) ),合適的時候再給余下的參數(shù)并且求值計算。

來看看我們都用過的currying, 我們經(jīng)常在綁定context 的時候?qū)崿F(xiàn)一個Function.prototype.bind函數(shù).

高階函數(shù)是實現(xiàn)currying的基礎, 所謂高階函數(shù)至少滿足這2個特性:
1,函數(shù)可以當作參數(shù)傳遞,
2,函數(shù)可以作為返回值。

Javascript在設計之初,參考了很多scheme語言的特性。而scheme是函數(shù)式語言鼻祖lisp的2大方言之一,所以javascript也擁有一些函數(shù)式語言的特性,包括高階函數(shù),閉包,lambda表達式等。

當javascript中的函數(shù)返回另一個函數(shù),此時會形成一個閉包,而在閉包中就可以保存第一次運算的參數(shù),我們用這個思想,來寫一個通用的currying函數(shù)。

我們約定, 當傳入?yún)?shù)時候, 繼續(xù)currying化, 參數(shù)為空時才開始求值.

假設在實現(xiàn)一個計算每月花費的函數(shù), 每天結(jié)束前我們都要記錄今天花了多少錢, 但我們只關(guān)心月底的花費總值, 無需每天都計算一次.

使用currying函數(shù), 便可以延遲到最后一刻才一起計算, 好處不言而喻, 在很多場合可以避免無謂的計算, 節(jié)省性能, 也是實現(xiàn)惰性求值的一種方案.

好了,現(xiàn)在才走進正題,

curring是預先填入一些參數(shù).

反curring就是把原來已經(jīng)固定的參數(shù)或者this上下文等當作參數(shù)延遲到未來傳遞.

其實就是搞這樣一個事情,將:

obj.foo( arg1 ) //foo本來是只在obj上的函數(shù). 就像push原本只在Array.prototype上

轉(zhuǎn)化成這樣的形式

foo( obj, arg1 ) // 跟我們舉的第一個例子一樣.將[].push轉(zhuǎn)換成push( [] )

就像原本是接在電視插頭上的插座,把它拆下來之后,其實也能用來接冰箱。

Ecma上Array和String的每個原型方法后面都有這么一段話,比如push:

NOTE The push function is intentionally generic; it does not require that its this value be an Array object.
Therefore it can be transferred to other kinds of objects for use as a method. Whether the concat function can be applied.

看下v8引擎里面Array.prototype.push的代碼:

復制代碼 代碼如下:

function ArrayPush() {
var n = TO_UINT32( this.length );
var m = %_ArgumentsLength();
for (var i = 0; i < m; i++) {
this[i+n] = %_Arguments(i); //屬性拷貝
this.length = n + m; //修正length
return this.length;
}
}

可以看到,ArrayPush方法沒有對this的類型做任何顯示的限制,所以理論上任何對象都可以被傳入ArrayPush這個訪問者。

我們需要解決的只剩下一個問題, 如何通過一種通用的方式來使得一個對象可以冒充array對象。

真正的實現(xiàn)代碼其實很簡單:

這段代碼雖然很短, 初次理解的時候還是有點費力. 我們拿push的例子看看它發(fā)生了什么.

復制代碼 代碼如下:

var push = Array.prototype.push.uncurrying();

push( obj, ‘first’ );

相關(guān)文章

最新評論