1. 定義
IIFE: Immediately Invoked Function Expression,意為立即調用的函數表達式,也就是說,聲明函數的同時立即調用這個函數。
對比一下,這是不采用IIFE時的函數聲明和函數調用:
function foo(){ var a = 10; console.log(a); } foo();
下面是IIFE形式的函數調用:
(function foo(){ var a = 10; console.log(a); })();
函數的聲明和IIFE的區別在於,在函數的聲明中,我們首先看到的是function關鍵字,而IIFE我們首先看到的是左邊的(。也就是說,使用一對()將函數的聲明括起來,使得JS編譯器不再認為這是一個函數聲明,而是一個IIFE,即需要立刻執行聲明的函數。
兩者達到的目的是相同的,都是聲明了一個函數foo並且隨后調用函數foo。
2. 為什么需要IIFE?
如果只是為了立即執行一個函數,顯然IIFE所帶來的好處有限。實際上,IIFE的出現是為了彌補JS在scope方面的缺陷:JS只有全局作用域(global scope)、函數作用域(function scope),從ES6開始才有塊級作用域(block scope)。對比現在流行的其他面向對象的語言可以看出,JS在訪問控制這方面是多么的脆弱!那么如何實現作用域的隔離呢?在JS中,只有function,只有function,只有function才能實現作用域隔離,因此如果要將一段代碼中的變量、函數等的定義隔離出來,只能將這段代碼封裝到一個函數中。
在我們通常的理解中,將代碼封裝到函數中的目的是為了復用。在JS中,當然聲明函數的目的在大多數情況下也是為了復用,但是JS迫於作用域控制手段的貧乏,我們也經常看到只使用一次的函數:這通常的目的是為了隔離作用域了!既然只使用一次,那么立即執行好了!既然只使用一次,函數的名字也省掉了!這就是IIFE的由來。
3. IIFE的常見形式
根據最后表示函數執行的一對()位置的不同,常見的IIFE寫法有兩種,示例如下:
列表1:IIFE寫法一
(function foo(){ var a = 10; console.log(a); })();
列表2:IIFE寫法二
(function foo(){ var a = 10; console.log(a); }());
這兩種寫法效果完全一樣,使用哪種寫法取決於你的風格,貌似第一種寫法比較常見。
其實,IIFE不限於()的表現形式[1],但是還是遵守約定俗成的習慣比較好。
4. IIFE的函數名和參數
根據《You Don’t Know JS:Scope & Clouses》[2]的說法,盡量避免使用匿名函數。但是IIFE確實只執行一次,給IIFE起個名字有些畫蛇添足了。如果非要給IIFE起個名字,干脆就叫IIFE好了。
IIFE可以帶(多個)參數,比如下面的形式:
var a = 2; (function IIFE(global){ var a = 3; console.log(a); // 3 console.log(global.a); // 2 })(window); console.log(a); // 2
5. IIFE構造單例模式
JS的模塊就是函數,最常見的模塊定義如下:
function myModule(){ var someThing = "123"; var otherThing = [1,2,3]; function doSomeThing(){ console.log(someThing); } function doOtherThing(){ console.log(otherThing); } return { doSomeThing:doSomeThing, doOtherThing:doOtherThing } } var foo = myModule(); foo.doSomeThing(); foo.doOtherThing(); var foo1 = myModule(); foo1.doSomeThing();
如果需要一個單例模式的模塊,那么可以利用IIFE:
var myModule = (function module(){ var someThing = "123"; var otherThing = [1,2,3]; function doSomeThing(){ console.log(someThing); } function doOtherThing(){ console.log(otherThing); } return { doSomeThing:doSomeThing, doOtherThing:doOtherThing } })(); myModule.doSomeThing(); myModule.doOtherThing();
6. 小結
IIFE的目的是為了隔離作用域,防止污染全局命名空間。
ES6以后也許有更好的訪問控制手段(模塊?類?),有待研究。
引用
1.Ben Alman, “Immediately-Invoked Function Expression (IIFE)“.
2.Kyle Simpson, You Don’t Know JS:Scope & Clouses (, 2014).
原文地址:http://softlab.sdut.edu.cn/blog/subaochen/2016/02/說一說js的iife/