你需要明白 IIFE 的原理,我簡單說一下:
function foo() {...} // 這是定義,Declaration;定義只是讓解釋器知道其存在,但是不會運行。foo(); // 這是語句,Statement;解釋器遇到語句是會運行它的。
IIFE 并非必須,傳統一點可以這么寫:
function foo() {...}
foo();
那么為什么要 IIFE?
- 傳統的方法啰嗦,定義和執行分開寫;
- 傳統的方法直接污染全局命名空間(瀏覽器里的?
global
?對象,如?window
)
于是,開發者們想找一個可以解決以上問題的寫法。那么像下面這么寫行不行呢?
function foo(...){}();
當然是不能,但是為什么呢?因為?function foo(...){}
?這個部分只是一個聲明,對于解釋器來說,就好像你寫了一個字符串?"function foo(...){}"
,它需要使用解析函數,比如?eval()
?來執行它才可以。所以把?()
?直接放在聲明后面是不會執行,這是錯誤的語法。
如何把它變得正確?說起來也簡單,只要把?聲明?變成?表達式(Expression)?就可以了。
實際上轉變表達式的辦法還是很多的,最常見的辦法是把函數聲明用一對?()
?包裹起來,于是就變成了:
(function foo() {...}) // 這里是故意換行,實際上可以和下面的括號連起來
();
這就等價于:
var foo = function () {...}; // 這就不是定義,而是表達式了。
foo();
但是之前我們說不行的那個寫法,其實也可以直接用括號包起來,這也是一種等價的表達式:
(function foo(){...}());
所以你問有沒有區別?很簡單:木有~
另外,剛才說過轉變表達式的方式很多,的確還有很多別的寫法,比如:
!function foo() {...}();
或者
+function foo() {...}();
這些都可以。
我個人挺偏愛用?void
?來轉變表達式,因為此關鍵字不會有返回值。不過這一點真的沒有什么要緊的,就當我“龜毛”好了……
void function () {// 這里是真正需要的代碼
}();
OK,所謂不去污染全局命名空間,是因為 IIFE 創建了一個新的函數作用域,你真正的業務代碼被封裝在其中,自然就不會觸碰到全局對象了。如果你需要全局對象,那就 pass 給 IIFE:
void function (global) {// 在這里,global 就是全局對象了
}(this) // 在瀏覽器里,this 就是 window 對象