|
|
|
|
|
在JavaScript的函數(shù)調(diào)用時(shí),常常見(jiàn)到 IIFE 這個(gè)詞,它其實(shí)是“立即調(diào)用函數(shù)表達(dá)式”的英文縮寫(xiě),在本文中,我將介紹什么是 IIFE,它與調(diào)用函數(shù)又有什么關(guān)系。
我們先看一個(gè)簡(jiǎn)單的函數(shù)調(diào)用示例,隨后再介紹 IIFE 與函數(shù)調(diào)用的關(guān)系。
function hello(name) {
return 'Hello ' + name + '!';
}
// 函數(shù)調(diào)用
const message = hello('World');
hello('World')
就是一個(gè)函數(shù)調(diào)用。
何為函數(shù)調(diào)用?
我們可以這樣定義:
當(dāng)計(jì)算結(jié)果為函數(shù)對(duì)象的表達(dá)式,后跟一對(duì)括號(hào)
()
,括號(hào)內(nèi)用逗號(hào),
分隔的參數(shù)表達(dá)式列表時(shí),這就是函數(shù)調(diào)用。例如 parseInt('18')。
上面示例中,hello
表達(dá)式計(jì)算為一個(gè)函數(shù)對(duì)象,后跟一對(duì)帶World
參數(shù)的括號(hào)()
。
函數(shù)調(diào)用表達(dá)式不能是屬性訪問(wèn)器 obj.myFunc()
,這是方法調(diào)用,例如[1,5].join(',')
不是函數(shù)調(diào)用,而是方法調(diào)用。請(qǐng)記住它們之間的區(qū)別。
函數(shù)調(diào)用示例
function myFunction(a, b) {
return a * b;
}
myFunction(10, 2); // 20
上面的函數(shù)不屬于任何對(duì)象。
在瀏覽器中,頁(yè)面對(duì)象是瀏覽器窗口。上面的函數(shù)自動(dòng)變成了窗口函數(shù)。因此,myFunction()
和 window.myFunction()
是同一個(gè)函數(shù):
function myFunction(a, b) {
return a * b;
}
window.myFunction(10, 2); // 20
何為 IIFE?
IIFE全稱是立即調(diào)用函數(shù)表達(dá)式(英文:immediately-invoked function expression,縮寫(xiě):IIFE),IIFE 也是一個(gè)函數(shù)調(diào)用。讓我們看一個(gè) IIFE 的簡(jiǎn)單例子:
// IIFE
const message = (function(name) {
return 'Hello ' + name + '!';
})('World');
第一對(duì)括號(hào)(function(name) {...})
是計(jì)算結(jié)果為函數(shù)對(duì)象的表達(dá)式,然后是一對(duì)帶'World
'參數(shù)的括號(hào):('World')
。
IIFE 用法
立即調(diào)用函數(shù)表達(dá)式擁有數(shù)種不同的寫(xiě)法。最常見(jiàn)的一種是將函數(shù)表達(dá)式字面量至于圓括號(hào)(分組運(yùn)算符)之內(nèi),然后使用圓括號(hào)調(diào)用函數(shù)。
(function() {
// 這里的語(yǔ)句將獲得新的作用域
})();
若要將作用域外變量傳遞進(jìn)函數(shù),則按下述方式書(shū)寫(xiě):
(function(a, b) {
// a == 'hello'
// b == 'world'
})('hello', 'world');
開(kāi)頭的括號(hào)可能會(huì)因?yàn)榻忉屍鞯姆痔?hào)自動(dòng)插入特性造成一些問(wèn)題。括號(hào)本用于明確字面量為表達(dá)式以與函數(shù)聲明語(yǔ)句區(qū)分,但解釋器可能將括號(hào)解釋為對(duì)以上一行中結(jié)尾的變量名為名的函數(shù)的調(diào)用。在一些省略分號(hào)的程序中,可見(jiàn)將分號(hào)至于行首的做法。這樣的分號(hào)被稱為“防御性分號(hào)”,舉例:
a = b + c
;(function() { // 故意將分號(hào)放在這里
// 代碼
})();
如此書(shū)寫(xiě),以防止語(yǔ)句被理解為對(duì)函數(shù)c的調(diào)用(c(...)
)。
IIFE 例子
理解立即調(diào)用函數(shù)表達(dá)式的關(guān)鍵在于認(rèn)清JavaScript擁有函數(shù)作用域,但沒(méi)有塊作用域(ES6之前),且通過(guò)指針(而非復(fù)制)將變量傳入一個(gè)函數(shù)閉包。 ES6 引入了新關(guān)鍵字 let
和 const
,用它們定義的常量和變量具有塊級(jí)作用域。
缺少塊作用域意味著一個(gè)在類(lèi)似于for循環(huán)的塊中聲明的變量會(huì)被置頂?shù)狡渌暮瘮?shù)中。如果一個(gè)內(nèi)部函數(shù)依賴于一個(gè)外部變量,而該外部變量被外部函數(shù)更改,那么執(zhí)行內(nèi)函數(shù)就有些困難。舉例,我們?cè)诼暶骱瘮?shù)之后,但在定義函數(shù)之前,改變一個(gè)變量的值。
var v, getValue;
v = 1;
getValue = function() { return v; };
v = 2;
getValue(); // 2
當(dāng)我們手動(dòng)給v
賦值時(shí)這結(jié)果似乎沒(méi)什么問(wèn)題。不過(guò),如果getValue()
是在一個(gè)循環(huán)中被定義的,那么就可能出現(xiàn)預(yù)想外的結(jié)果。
var v, getValue;
v = 1;
getValue = (function(x) {
return function() { return x; };
})(v);
v = 2;
getValue(); // 1
此例中,函數(shù)將 v
作為參數(shù)傳入并立即調(diào)用,保護(hù)了內(nèi)部函數(shù)的執(zhí)行上下文。
立即調(diào)用函數(shù)表達(dá)式也可以用來(lái)創(chuàng)建私有方法來(lái)訪問(wèn)函數(shù),不僅起到保護(hù)作用,同時(shí)也暴露了一些可以后續(xù)使用的屬性。
// 'counter' 函數(shù)返回一個(gè)具有屬性的對(duì)象, 這里的屬性就是
// get set等函數(shù)
var counter = (function(){
var i = 0;
return {
get: function(){
return i;
},
set: function( val ){
i = val;
},
increment: function() {
return ++i;
}
};
})();
// 這些調(diào)用使用了剛才counter得到的屬性
counter.get(); // 0
counter.set( 3 );
counter.increment(); // 4
counter.increment(); // 5
如果我們?cè)噲D從全局作用域直接訪問(wèn) counter.i
,會(huì)得到 undefined
,因?yàn)?i 這個(gè)數(shù)據(jù)由 IIFE 封裝,它并不是 counter
的屬性。同樣的,如果我們?cè)噲D訪問(wèn) i
也會(huì)收到錯(cuò)誤,因?yàn)?i
并沒(méi)有在全局作用域中定義。
總結(jié)
本文介紹了JavaScript中的 IIFE 與函數(shù)調(diào)用,通過(guò)本文的學(xué)習(xí),我們應(yīng)該了解了函數(shù)調(diào)用的定義,知道 IIFE 也是函數(shù)調(diào)用的一種。
相關(guān)文章