function與感嘆號

2022-04-22 07:41:50 字數 4261 閱讀 5775

!function()()        // true

在控制台執行後得到的值時true,為什麼是true這很容易理解,因為這個匿名函式沒有返回值,預設返回的就是undefined,求反的結果很自然的就是true。所以問題並不在於結果值,而是在於,為什麼求反操作能夠讓乙個匿名函式的自調製的合法?

平時我們可能對新增括號來呼叫匿名函式的方式更為習慣:

(function())()        // true

或者:

(function()())        // true

雖然上述兩者括號的位置不同,不過效果完全一樣。

那麼,是什麼好處使得為數不少的人對這種嘆號的方式情有獨鍾?如果只是為了節約乙個字元未免太沒有必要了,這樣算來即使乙個100k的庫恐怕也節省不了多少空間。既然不是空間,那麼就是說也許還有時間上的考量,事實很難說清,文章的最後有提到效能。

回到核心問題,為什麼能這麼做?甚至更為核心的問題是,為什麼必須這麼做?

其實無論是括號,還是感嘆號,讓整個語句合法做的事情只有一件,就是讓乙個函式宣告語句變成了乙個表示式。

function a()        // undefined  

這是乙個函式宣告,如果在這麼乙個宣告後直接加上括號呼叫,解析器自然不會理解而報錯:

function a()()        // syntaxerror: unexpected_token  

因為這樣的**混淆了函式宣告和函式呼叫,以這種方式宣告的函式a,就應該以a();的方式呼叫。

但是括號則不同,它將乙個函式宣告轉化成了乙個表示式,解析器不再以函式宣告的方式處理函式a,而是作為乙個函式表示式處理,也因此只有在程式執行到函式a時它才能被訪問。

所以,任何消除函式宣告和函式表示式間歧義的方法,都可以被解析器正確識別。比如:

var i = function()();        // undefined  

1 && function()(); // true

1, function()(); // undefined

賦值,邏輯,甚至是逗號,各種操作符都可以告訴解析器,這個不是函式宣告,它是個函式表示式。並且,對函式一元運算可以算的上是消除歧義最快的方式,感嘆號只是其中之一,如果不在乎返回值,這些一元運算都是有效的:

!function()()        // true

+function()() // nan

-function()() // nan

~function()() // -1

甚至下面這些關鍵字,都能很好的工作:

void function()()        // undefined  

new function()() // object

delete function()() // true

最後,括號做的事情也是一樣的,消除歧義才是它真正的工作,而不是把函式作為乙個整體,所以無論括號括在宣告上還是把整個函式都括在裡面,都是合法的:

(function())()        // undefined

(function()()) // undefined

說了這麼多,實則在說的一些都是最為基礎的概念——語句,表示式,表示式語句,這些概念如同指標與指標變數一樣容易產生混淆。雖然這種混淆對程式設計無表徵影響,但卻是一塊絆腳石隨時可能因為它而頭破血流。

最後討論下效能。我在jsperf上簡單建立了乙個測試: ,可以用不同瀏覽器訪問,執行測試檢視結果。我也同時將結果羅列如下表所示(由於我比較窮,測試配置有點丟人不過那也沒辦法:奔騰雙核1.4g,2g記憶體,win7企業版):

option

code

ops/sec

chrome 13

firefox 6

ie9safari 5

!!function()()

3,773,196

10,975,198

572,694

2,810,197

++function()()

21,553,847

12,135,960

572,694

1,812,238

--function()()

21,553,847

12,135,960

572,694

1,864,155

~~function()()

3,551,136

3,651,652

572,694

1,876,002

(1)(function())()

3,914,953

12,135,960

572,694

3,025,608

(2)(function()())

4,075,201

12,135,960

572,694

3,025,608

void

void function()()

4,030,756

12,135,960

572,694

3,025,608

newnew function()()

619,606

299,100

407,104

816,903

delete

delete function()()

4,816,225

12,135,960

572,694

2,693,524

=var i = function()()

4,984,774

12,135,960

565,982

2,602,630

&&1 && function()()

5,307,200

4,393,486

572,694

2,565,645

||0 || function()()

5,000,000

4,406,035

572,694

2,490,128

&1 & function()()

4,918,209

12,135,960

572,694

1,705,551

|1 | function()()

4,859,802

12,135,960

572,694

1,612,372

^1 ^ function()()

4,654,916

12,135,960

572,694

1,579,778

,1, function()()

4,878,193

12,135,960

572,694

2,281,186

可見不同的方式產生的結果並不相同,而且,差別很大,因瀏覽器而異。

但我們還是可以從中找出很多共性:new方法永遠最慢——這也是理所當然的。其它方面很多差距其實不大,但有一點可以肯定的是,感嘆號並非最為理想的選擇。反觀傳統的括號,在測試裡表現始終很快,在大多數情況下比感嘆號更快——所以平時我們常用的方式毫無問題,甚至可以說是最優的。加減號在chrome表現驚人,而且在其他瀏覽器下也普遍很快,相比感嘆號效果更好。

當然這只是個簡單測試,不能說明問題。但有些結論是有意義的:括號和加減號最優。

但是為什麼這麼多開發者鍾情於感嘆號?我覺得這只是乙個習慣問題,它們之間的優劣完全可以忽略。一旦習慣了一種**風格,那麼這種約定會使得程式從混亂變得可讀。如果習慣了感嘆號,我不得不承認,它比括號有更好的可讀性。我不用在閱讀時留意括號的匹配,也不用在編寫時粗心遺忘——

當我也這麼幹然後嚷嚷著這居然又節省了乙個字元而沾沾自喜的時候,卻忘了自己倉皇翻出一本卷邊的c語言教科書的窘迫和荒唐......任何人都有忘記的時候,當再撿起來的時候,撿起的就已經不單單是忘掉的東西了。

2011-10-31更新:如果你使用aptana,那麼在使用(!+-)時要注意一點,它們會讓aptana的解析失效,導致outline視窗沒有任何顯示。但是就**本身而言,其執行沒有任何問題。

function與感嘆號

如果在function之前加上感嘆號 會怎麼樣?比如下面的 function true 在控制台執行後得到的值時true,為什麼是true這很容易理解,因為這個匿名函式沒有返回值,預設返回的就是undefined,求反的結果很自然的就是true。所以問題並不在於結果值,而是在於,為什麼求反操作能夠讓...

function與感嘆號

最近有空可以讓我靜下心來看看各種 function與感嘆號的頻繁出現,讓我回想起2個月前我回杭州最後參加團隊會議的時候,西子劍影丟擲的一樣的問題 如果在function之前加上感嘆號 會怎麼樣?比如下面的 function true 在控制台執行後得到的值時true,為什麼是true這很容易理解,因...

function與感嘆號

通常,我們呼叫乙個方法的方式就是 functionname 但是,如果我們嘗試為乙個 定義函式 末尾加上 解析器是無法理解的。function msg 解析器是無法理解的 定義函式的呼叫方式應該是 msg 那為什麼將函式體部分用 包裹起來就可以了呢?原來,使用括號包裹定義函式體,解析器將會以函式表示...