我在寫代碼時候經常會在tpl的<script>里寫類似的代碼: 剛開始的時候只知道寫了它不需要調用,直接執行,就這樣依葫蘆畫瓢,我寫了很多代碼。說道這,還要說說這貨的載入順序,如果把代碼直接寫到script標簽里,當頁面載入完這個script標簽就會執行裡邊的代碼了。如果在這代碼里用到了未載入的do ...
我在寫代碼時候經常會在tpl的<script>里寫類似的代碼:
$(function(){ alert("我好餓"); });
剛開始的時候只知道寫了它不需要調用,直接執行,就這樣依葫蘆畫瓢,我寫了很多代碼。說道這,還要說說這貨的載入順序,如果把代碼直接寫到script標簽里,當頁面載入完這個script標簽就會執行裡邊的代碼了。如果在這代碼里用到了未載入的dom或者調用了未載入的方法,是會報錯的。言歸正傳,這個函數其實就是自執行函數,很多人會比較專業地稱為“立即調用的函數表達式”。
在這裡,我引用下limlimlim的博客,感覺比較專業...原址http://blog.csdn.net/limlimlim/article/details/9198111
當你聲明類似function foo(){}或var foo = function(){}函數的時候,通過在後面加個括弧就可以實現自執行,例如foo(),看代碼:
// 因為想下麵第一個聲明的function可以在後面加一個括弧()就可以自己執行了,比如foo(), // 因為foo僅僅是function() { /* code */ }這個表達式的一個引用 var foo = function(){ /* code */ } // ...是不是意味著後面加個括弧都可以自動執行? function(){ /* code */ }(); // SyntaxError: Unexpected token ( //
上述代碼,如果甚至運行,第2個代碼會出錯,因為在解析器解析全局的function或者function內部function關鍵字的時候,預設是認為function聲明,而不是function表達式,如果你不顯示告訴編譯器,它預設會聲明成一個缺少名字的function,並且拋出一個語法錯誤信息,因為function聲明需要一個名字。
有趣的是,即便你為上面那個錯誤的代碼加上一個名字,他也會提示語法錯誤,只不過和上面的原因不一樣。在一個表達式後面加上括弧(),該表達式會立即執行,但是在一個語句後面加上括弧(),是完全不一樣的意思,他的只是分組操作符。
// 下麵這個function在語法上是沒問題的,但是依然只是一個語句 // 加上括弧()以後依然會報錯,因為分組操作符需要包含表達式 function foo(){ /* code */ }(); // SyntaxError: Unexpected token ) // 但是如果你在括弧()里傳入一個表達式,將不會有異常拋出 // 但是foo函數依然不會執行 function foo(){ /* code */ }( 1 ); // 因為它完全等價於下麵這個代碼,一個function聲明後面,又聲明瞭一個毫無關係的表達式: function foo(){ /* code */ } ( 1 );
要解決上述問題,非常簡單,我們只需要用大括弧將代碼的代碼全部括住就行了,因為JavaScript里括弧()裡面不能包含語句,所以在這一點上,解析器在解析function關鍵字的時候,會將相應的代碼解析成function表達式,而不是function聲明。
// 下麵2個括弧()都會立即執行 (function () { /* code */ } ()); // 推薦使用這個 (function () { /* code */ })(); // 但是這個也是可以用的 // 由於括弧()和JS的&&,異或,逗號等操作符是在函數表達式和函數聲明上消除歧義的 // 所以一旦解析器知道其中一個已經是表達式了,其它的也都預設為表達式了 // 不過,請註意下一章節的內容解釋 var i = function () { return 10; } (); true && function () { /* code */ } (); 0, function () { /* code */ } (); // 如果你不在意返回值,或者不怕難以閱讀 // 你甚至可以在function前面加一元操作符號 !function () { /* code */ } (); ~function () { /* code */ } (); -function () { /* code */ } (); +function () { /* code */ } (); // 還有一個情況,使用new關鍵字,也可以用,但我不確定它的效率 // http://twitter.com/kuvos/status/18209252090847232 new function () { /* code */ } new function () { /* code */ } () // 如果需要傳遞參數,只需要加上括弧()
上面所說的括弧是消除歧義的,其實壓根就沒必要,因為括弧本來內部本來期望的就是函數表達式,但是我們依然用它,主要是為了方便開發人員閱讀,當你讓這些已經自動執行的表達式賦值給一個變數的時候,我們看到開頭有括弧(,很快就能明白,而不需要將代碼拉到最後看看到底有沒有加括弧。
在這篇帖子里,我們一直叫自執行函數,確切的說是自執行匿名函數(Self-executing anonymous function),但英文原文作者一直倡議使用立即調用的函數表達式(Immediately-Invoked Function Expression)這一名稱,作者又舉了一堆例子來解釋,好吧,我們來看看:
// 這是一個自執行的函數,函數內部執行自身,遞歸 function foo() { foo(); } // 這是一個自執行的匿名函數,因為沒有標示名稱 // 必須使用arguments.callee屬性來執行自己 var foo = function () { arguments.callee(); }; // 這可能也是一個自執行的匿名函數,僅僅是foo標示名稱引用它自身 // 如果你將foo改變成其它的,你將得到一個used-to-self-execute匿名函數 var foo = function () { foo(); }; // 有些人叫這個是自執行的匿名函數(即便它不是),因為它沒有調用自身,它只是立即執行而已。 (function () { /* code */ } ()); // 為函數表達式添加一個標示名稱,可以方便Debug // 但一定命名了,這個函數就不再是匿名的了 (function foo() { /* code */ } ()); // 立即調用的函數表達式(IIFE)也可以自執行,不過可能不常用罷了 (function () { arguments.callee(); } ()); (function foo() { foo(); } ()); // 另外,下麵的代碼在黑莓5里執行會出錯,因為在一個命名的函數表達式里,他的名稱是undefined // 呵呵,奇怪 (function foo() { foo(); } ());
希望這裡的一些例子,可以讓大家明白,什麼叫自執行,什麼叫立即調用。(我到這也才理解了一點,理論萬歲)
下麵 limlimlim還講了Module模式,這是我之後要學習的重要東西,當然在這之前還有閉包(頭大)
給各位看看例子吧
// 創建一個立即調用的匿名函數表達式 // return一個變數,其中這個變數里包含你要暴露的東西 // 返回的這個變數將賦值給counter,而不是外面聲明的function自身 var counter = (function () { var i = 0; return { get: function () { return i; }, set: function (val) { i = val; }, increment: function () { return ++i; } }; } ()); // counter是一個帶有多個屬性的對象,上面的代碼對於屬性的體現其實是方法 counter.get(); // 0 counter.set(3); counter.increment(); // 4 counter.increment(); // 5 counter.i; // undefined 因為i不是返回對象的屬性 i; // 引用錯誤: i 沒有定義(因為i只存在於閉包)
另外附上原版湯姆大叔的地址:http://www.cnblogs.com/TomXu/archive/2011/12/31/2289423.html