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