最新文章專題視頻專題問(wèn)答1問(wèn)答10問(wèn)答100問(wèn)答1000問(wèn)答2000關(guān)鍵字專題1關(guān)鍵字專題50關(guān)鍵字專題500關(guān)鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關(guān)鍵字專題關(guān)鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
問(wèn)答文章1 問(wèn)答文章501 問(wèn)答文章1001 問(wèn)答文章1501 問(wèn)答文章2001 問(wèn)答文章2501 問(wèn)答文章3001 問(wèn)答文章3501 問(wèn)答文章4001 問(wèn)答文章4501 問(wèn)答文章5001 問(wèn)答文章5501 問(wèn)答文章6001 問(wèn)答文章6501 問(wèn)答文章7001 問(wèn)答文章7501 問(wèn)答文章8001 問(wèn)答文章8501 問(wèn)答文章9001 問(wèn)答文章9501
當(dāng)前位置: 首頁(yè) - 科技 - 知識(shí)百科 - 正文

JavaScript中有趣的反柯里化

來(lái)源:懂視網(wǎng) 責(zé)編:小采 時(shí)間:2020-11-27 20:30:41
文檔

JavaScript中有趣的反柯里化

JavaScript中有趣的反柯里化:反科里化的話題來(lái)自javascript之父Brendan Eich去年的一段twitter. 近幾天研究了一下,覺(jué)得這個(gè)東東非常有意思,分享一下。先忘記它的名字,看下它能做什么.不要小看這個(gè)功能,試想下,我們?cè)趯懸粋€(gè)庫(kù)的時(shí)候,時(shí)常會(huì)寫這樣的代碼,拿webQQ的Jx庫(kù)舉例。
推薦度:
導(dǎo)讀JavaScript中有趣的反柯里化:反科里化的話題來(lái)自javascript之父Brendan Eich去年的一段twitter. 近幾天研究了一下,覺(jué)得這個(gè)東東非常有意思,分享一下。先忘記它的名字,看下它能做什么.不要小看這個(gè)功能,試想下,我們?cè)趯懸粋€(gè)庫(kù)的時(shí)候,時(shí)常會(huì)寫這樣的代碼,拿webQQ的Jx庫(kù)舉例。
反科里化的話題來(lái)自javascript之父Brendan Eich去年的一段twitter. 近幾天研究了一下,覺(jué)得這個(gè)東東非常有意思,分享一下。先忘記它的名字,看下它能做什么.

20120301085946697.png

不要小看這個(gè)功能,試想下,我們?cè)趯懸粋€(gè)庫(kù)的時(shí)候,時(shí)常會(huì)寫這樣的代碼,拿webQQ的Jx庫(kù)舉例。

20120301085946697.png

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

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

20120301085946697.png

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

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

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

20120301085946697.png

javascript里可以寫的很接近.

20120301085946697.png

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

20120301085946697.png

如果用uncurrying的話, 就可以

20120301085946697.png

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

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

20120301085946697.png

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

接下來(lái)一步一步來(lái)看看原理以及實(shí)現(xiàn)。

在了解反currying化這個(gè)奇怪的名字之前,我們得先搞清楚currying。

維基百科上的定義:科里化( currying ); 又稱部分求值,是把接受多個(gè)參數(shù)的函數(shù)變換成接受一個(gè)單一參數(shù)的函數(shù),并且返回接受余下的參數(shù)并且返回結(jié)果的新函數(shù)的技術(shù)。

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

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

20120301085946697.png

高階函數(shù)是實(shí)現(xiàn)currying的基礎(chǔ), 所謂高階函數(shù)至少滿足這2個(gè)特性:

1, 函數(shù)可以當(dāng)作參數(shù)傳遞,

2, 函數(shù)可以作為返回值。

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

當(dāng)javascript中的函數(shù)返回另一個(gè)函數(shù),此時(shí)會(huì)形成一個(gè)閉包,而在閉包中就可以保存第一次運(yùn)算的參數(shù),我們用這個(gè)思想,來(lái)寫一個(gè)通用的currying函數(shù)。

20120301085946697.png

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

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

20120301085946697.png

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

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

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

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

其實(shí)就是搞這樣一個(gè)事情,將:

1

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

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

1

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

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

Ecma上Array和String的每個(gè)原型方法后面都有這么一段話,比如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.

Javascript為什么要這樣設(shè)計(jì), 我們先來(lái)復(fù)習(xí)下動(dòng)態(tài)語(yǔ)言中重要的鴨子類型思想.

說(shuō)個(gè)故事:

很久以前有個(gè)皇帝喜歡聽鴨子呱呱叫,于是他召集大臣組建一個(gè)一千只鴨子的合唱團(tuán)。大臣把全國(guó)的鴨子都抓來(lái)了,最后始終還差一只。有天終于來(lái)了一只自告奮勇的雞,這只雞說(shuō)它也會(huì)呱呱叫,好吧在這個(gè)故事的設(shè)定里,它確實(shí)會(huì)呱呱叫。 后來(lái)故事的發(fā)展很明顯,這只雞混到了鴨子的合唱團(tuán)中?!?皇帝只是想聽呱呱叫,他才不在乎你是鴨子還是雞呢。

這個(gè)就是鴨子類型的概念,在javascript里面,很多函數(shù)都不做對(duì)象的類型檢測(cè),而是只關(guān)心這些對(duì)象能做什么。

Array構(gòu)造器和String構(gòu)造器的prototype上的方法就被特意設(shè)計(jì)成了鴨子類型。這些方法不對(duì)this的數(shù)據(jù)類型做任何校驗(yàn)。這也就是為什么arguments能冒充array調(diào)用push方法.

看下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方法沒(méi)有對(duì)this的類型做任何顯示的限制,所以理論上任何對(duì)象都可以被傳入ArrayPush這個(gè)訪問(wèn)者。

我們需要解決的只剩下一個(gè)問(wèn)題, 如何通過(guò)一種通用的方式來(lái)使得一個(gè)對(duì)象可以冒充array對(duì)象。

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

20120301085946697.png

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

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

push( obj, ‘first’ );

20120301085946697.png

聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問(wèn)題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

文檔

JavaScript中有趣的反柯里化

JavaScript中有趣的反柯里化:反科里化的話題來(lái)自javascript之父Brendan Eich去年的一段twitter. 近幾天研究了一下,覺(jué)得這個(gè)東東非常有意思,分享一下。先忘記它的名字,看下它能做什么.不要小看這個(gè)功能,試想下,我們?cè)趯懸粋€(gè)庫(kù)的時(shí)候,時(shí)常會(huì)寫這樣的代碼,拿webQQ的Jx庫(kù)舉例。
推薦度:
標(biāo)簽: js 有趣 有意思
  • 熱門焦點(diǎn)

最新推薦

猜你喜歡

熱門推薦

專題
Top