由於非常感興趣, 我查詢了很多關於IIFE (immediately-invoked function expression)的東西, 如下:
(function (window, document, undefined) {// })(window, document);
那么為什么不寫一篇關於它的文章呢? ;-)
首先,它有一系列不同的東西。從頭開始:
作用域
JavaScript有function
作用域, 所以它被用在必須私有作用域的地方。舉個例子:
(function (window, document, undefined) {var name = 'Todd';})(window, document);console.log(name); // name沒有定義, 它在一個不同的作用域里面
Simple.
它的工作原理
一個普通的函數是這樣的:
var logMyName = function (name) {console.log(name);};logMyName('Todd');
我們可以調用它的范圍和我們提供的作用域沒有關系。“IIFE”被創造的原因是它們是立即執行表達式,這意味着他們一創建就馬上被執行,當然我們不能再次執行它們。 如下:
var logMyName = (function (name) {console.log(name); // Todd})('Todd');
這里的秘密武器是這個, (還記得上面分配了一個變量的例子嗎?):
(function () {})();
如果沒有額外的小括號,他們就不能工作:
function () {}();
雖然有一些技巧可以強制讓javascript讓它工作,如下使用!
字符:
!function () {}();
當然還有下面的變體:
+function () {}();-function () {}();~function () {}();
但是我不會使用他們.
查看 Disassembling JavaScript’s IIFE Syntax 這是一篇詳細介紹IIFE語法和它的變體的文章。
參數
現在我們知道它是怎么工作了,我們可以往IIFE中傳參:
(function (window) {})(window);
這個的工作原理是什么? 注意, 最后的 (window);
是函數的調用, 並且我們傳入了一個window
對象。它通過這個傳入到函數的變量,這個變量我們也命名為window
。 你可以認為這個是沒有意義的,但是現在我們使用window
更好. 那么我們還能做什么? 我們可以傳入更多的東西, 讓我們傳入一個document
對象:
(function (window, document) {// we refer to window and document normally})(window, document);
局部變量的調用比全局變量更快, 當然這是在超大規模的情況下,一般情況我們不會感覺到速度的影響 - 但是如果我們用全局變量很多也是值得考慮的。
undefined
? 什么是
在ECMAScript 3, undefined是變量
. 意味着它的值可以被定義,比如 undefined = true;
在 ECMAScript 5使用嚴格遵守模式('use strict';
) 這個賦值會報錯.:
(function (window, document, undefined) {})(window, document);
下面是可以得:
undefined = true;(function (window, document, undefined) {// undefined is a local undefined variable})(window, document);
壓縮
壓縮我們的變量是IIFE模式真正實用的地方。局部變量的名字可以在它們傳入函數中進行改變,所以我們如下調用:
(function (window, document, undefined) {console.log(window); // Object window})(window, document);
變為:
(function (a, b, c) {console.log(a); // Object window})(window, document);
想象下,所有的你引入的類庫里面的window
和 document
很好的壓縮。 如下面的jquery類庫的調用:
(function ($, window, document, undefined) {// use $ to refer to jQuery// $(document).addClass('test');})(jQuery, window, document);
(function (a, b, c, d) {// becomes// a(c).addClass('test');})(jQuery, window, document);
這意味着你不用使用 jQuery.noConflict();
任何 $
都會執行jquery. 學習這個對於變量的作用域會對你幫助很大。一個好的壓縮工具會把undefined壓縮為c。undefined 的命名是無關緊要的,我們只是需要用它來判斷一個對象是否有值或者有沒有定值而已
非瀏覽器全局環境
比如Node.js, 瀏覽器不在是全局對象,我們會使用IIFE讓他在跨環境運行:
(function (root) {})(this);
在瀏覽器中,this
指向 window
對象, 所以我們不需要傳入 window
, 我們可以使用this來縮短
. 我喜歡使用root來命名它,如果在非瀏覽器環境也合適
。 如果你對普通的解決方案感興趣 這里是一個UMD包裝:
(function (root, factory) {if (typeof define === 'function' && define.amd) {define(factory);} else if (typeof exports === 'object') {module.exports = factory;} else {root.MYMODULE = factory();}})(this, function () {// });
在瀏覽器, root.MYMODULE = factory();
是我們的IIFE模塊, 其他情況(如 Node.js) 它會調用 module.exports
或者當 typeof define === 'function' && define.amd為true 則調用
requireJS。當然這個另外一個故事了, 我強烈推薦你看這篇文章 UMD repo.