這次帶給大家js的(function(){xxx})()使用詳解,js的(function(){xxx})()使用的注意事項有哪些,下面就是實戰案例,一起來看一下。
自執行匿名函數:
常見格式:(function() { /* code */ })();
解釋:包圍函數(function(){})的第一對括號向腳本傳回未命名的函數,隨後一對空括號立即執行傳回的未命名函數,括號內為匿名函數的參數。
作用:可以用它來建立命名空間,只要把自己所有的程式碼都寫在這個特殊的函式包裝內,那麼外部就不能訪問,除非你允許(變數前加上window,這樣函數或變數就成為全域)。各JavaScript函式庫的程式碼也基本上是這種組織形式。
總結一下,執行函數的作用主要為 匿名 和 自動執行,程式碼在解釋時就已經在運作了。
其他寫法
<span style='font-family: 微软雅黑, "Microsoft YaHei"; font-size: 14px;'>(function () { /* code */ } ()); <br>!function () { /* code */ } ();<br>~function () { /* code */ } ();<br>-function () { /* code */ } ();<br>+function () { /* code */ } ();</span>
最近有空可以讓我靜下心來看看各種代碼,function與感嘆號的頻繁出現,讓我回想起2個月前我回杭州最後參加團隊會議的時候,@西子劍影拋出的一樣的問題:如果在function之前加上感嘆號(!) 會怎麼樣?例如下面的程式碼:
<span style='font-family: 微软雅黑, "Microsoft YaHei"; font-size: 14px;'>!function(){alert('iifksp')}() // true</span>
在控制台運行後得到的值時true,為什麼是true這很容易理解,因為這個匿名函數沒有回傳值,預設回傳的就是undefined ,求反的結果很自然的就是true。所以問題不在於結果值,而是在於,為什麼求反操作能夠讓一個匿名函數的自調變的合法性?
平時我們可能會對新增括號來呼叫匿名函數的方式更為習慣:
<span style='font-family: 微软雅黑, "Microsoft YaHei"; font-size: 14px;'>(function(){alert('iifksp')})() // true</span>
或:##
<span style='font-family: 微软雅黑, "Microsoft YaHei"; font-size: 14px;'>(function(){alert('iifksp')}()) // true</span>
雖然上述兩者括號的位置不同,不過效果完全一樣。
那麼,是什麼好處使得為數不少的人對這種嘆號的方式情有獨鍾?如果只是為了節省一個字元未免太沒有必要了,這樣算來即使一個100K的庫恐怕也節省不了多少空間。既然不是空間,就是說也許還有時間上的考量,事實很難說清,文章的最後有提到性能。
回到核心問題,為什麼可以這麼做?甚至更核心的問題是,為什麼必須這麼做?
其實無論是括號,或是感嘆號,讓整個語句合法做的事情只有一件,就是讓一個函數宣告語句變成了一個表達式。
<span style='font-family: 微软雅黑, "Microsoft YaHei"; font-size: 14px;'>function a(){alert('iifksp')} // undefined</span>
这是一个函数声明,如果在这么一个声明后直接加上括号调用,解析器自然不会理解而报错:
<span style='font-family: 微软雅黑, "Microsoft YaHei"; font-size: 14px;'>function a(){alert('iifksp')}() // SyntaxError: unexpected_token</span>
因为这样的代码混淆了函数声明和函数调用,以这种方式声明的函数 a,就应该以 a(); 的方式调用。
但是括号则不同,它将一个函数声明转化成了一个表达式,解析器不再以函数声明的方式处理函数a,而是作为一个函数表达式处理,也因此只有在程序执行到函数a时它才能被访问。
所以,任何消除函数声明和函数表达式间歧义的方法,都可以被解析器正确识别。比如:
<span style='font-family: 微软雅黑, "Microsoft YaHei"; font-size: 14px;'>var i = function(){return 10}(); // undefined 1 && function(){return true}(); // true 1, function(){alert('iifksp')}(); // undefined</span>
赋值,逻辑,甚至是逗号,各种操作符都可以告诉解析器,这个不是函数声明,它是个函数表达式。并且,对函数一元运算可以算的上是消除歧义最快的方式,感叹号只是其中之一,如果不在乎返回值,这些一元运算都是有效的:
<span style='font-family: 微软雅黑, "Microsoft YaHei"; font-size: 14px;'>!function(){alert('iifksp')}() // true+function(){alert('iifksp')}() // NaN-function(){alert('iifksp')}() // NaN~function(){alert('iifksp')}() // -1</span>
甚至下面这些关键字,都能很好的工作:
<span style='font-family: 微软雅黑, "Microsoft YaHei"; font-size: 14px;'>void function(){alert('iifksp')}() // undefined new function(){alert('iifksp')}() // Object delete function(){alert('iifksp')}() // true</span>
最后,括号做的事情也是一样的,消除歧义才是它真正的工作,而不是把函数作为一个整体,所以无论括号括在声明上还是把整个函数都括在里面,都是合法的:
<span style='font-family: 微软雅黑, "Microsoft YaHei"; font-size: 14px;'>(function(){alert('iifksp')})() // undefined(function(){alert('iifksp')}()) // undefined</span>
说了这么多,实则在说的一些都是最为基础的概念——语句,表达式,表达式语句,这些概念如同指针与指针变量一样容易产生混淆。虽然这种混淆对编程无表征影响,但却是一块绊脚石随时可能因为它而头破血流。
最后讨论下性能。我在jsperf上简单建立了一个测试:http://jsperf.com/js-funcion-expression-speed ,可以用不同浏览器访问,运行测试查看结果。我也同时将结果罗列如下表所示(由于我比较穷,测试配置有点丢人不过那也没办法:奔腾双核1.4G,2G内存,win7企业版):
選項 | #Ops/sec | ||||
---|---|---|---|---|---|
#Chrome 13 | Firefox 6 | IE9 | |||
#Safari 5 | |||||
! | #!function(){;}() | 3,773,196 | 10,975,198 | 572,694 | 2,810,197 |
## 函數(){;}()#########21,553,847## ##########12,135,960#############572,694############1,812,238#################1,812,238##################1,812,238############# # #####-##########-功能(){;}() | 21,553,847 | #12,135,960 | #572,694 | #1,864,155 | |
~ | ~function(){;}() | #3,551,136 | 3,651,652 | 572,694 | 1,876,002 |
(1) | (函數(){;})() | # #3,914,953 | 12,135,960 | 572,694 | |
(函數(){;}()) | 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 |
##new | new function(){;}() | 619,606 | 299,100 | 407,104 | 816,903 |
##刪除 | 刪除函數(){;}() | 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 && 函數(){;}() | #5,307,200 | 4,393,486 | 572,694 | #2,565,645 |
2,565,645 | ######2,565,645##################2,565,645####### ### #########||############0 ||功能(){;}() | 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 |函數(){;}() | 4,859,802 | ##12,135,960##572,694 | 1,612,372 | |
1 ^ 函數(){;}() | 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語言教科書的窘迫和荒唐......任何人都有忘記的時候,當再撿起來的時候,撿起的就已經不單單是忘掉的東西了。
我相信看了本文案例你已經掌握了方法,更多精彩請關注php中文網其它相關文章!
推薦閱讀:
以上是js的(function(){xxx})()使用詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!