今天來看看IIFE
IIFE全名為
Immediately Invoked Functions Expressions
指的是可以立即執行的Functions Expressions
函式表示式,中文多譯為立即(執行)函式。
來看看以下程式碼
var hello = function(name){
console.log('Hello ' + name);
};
這是一個Functions Expressions
函式表示式,要呼叫它通常會寫成hello()
hello();
目前沒有傳值進去,所以函式印出Hello undefined
若把hello()
這句刪掉,把程式碼改成這樣:
var hello = function(name){
console.log('Hello ' + name);
}();
電腦在函式表示式後面讀到()
,就知道要立刻呼叫這個函式,這種立刻執行的函式寫法就稱為IIFE。
若要傳值進去可以加參數在最後面的()
var hello = function(name){
console.log('Hello ' + name);
}('Simon');
再看看另一個例子,將函式裏頭的console.log
改成return
,透過另一個console.log()
呼叫變數指向的函式。
一般的函式表示式
var hello2 = function(name){
return 'Hello ' + name
};
console.log(hello2);
函式結尾的}
後面沒有()
表示立即執行,所以console.log
印出hello2時,是印出hello2指向的函式
IIFE
var hello2 = function(name){
return 'Hello ' + name
}();
console.log(hello2)
函式結尾的}
後面有()
表示立即執行,當console.log
印出hello2時,是印出hello2指向函式立即執行的結果值。
若要傳值進去,一樣加參數在函式最後面的()
var hello2 = function(name){
return 'Hello ' + name
}('Simon');
console.log(hello2);
再來,透過變數指向立即執行函式,要呼叫它只要透過變數名hello2
就好,不用像一般的Functions Expressions
寫成hello2()
,那現在如果寫成這樣會發生什麼事呢?
var hello2 = function(name){
return 'Hello ' + name
}('Simon');
console.log(hello2());
hello2乍看指向立即執行函式,其實可視為指向立即執行函式的返回值Hello Simon
,現在console.log()
的hello2後面多加一個()
,電腦就會往return回傳的'Hello ' + name
去找函式,現在'Hello ' + name
是字串Hello Simon
,並不是函式,自然就報出錯誤囉!
再看看以下程式碼,在第16天筆記,我們知道Expression
表示式會產生(回傳)一個值,而這個值不一定會被開發者用變數指著。
現在我們不用變數指著IIFE,這種寫法可以嗎?
function(food){
console.log('大俠愛吃' + food)
}('漢堡包');
因為沒有變數指向它(不是Functions Expressions
),它也沒有名子(不是Function Statement
),所以語法解析器認為這段程式寫錯了,自然報錯。
解決方法是,最外面再用一個()
把它包起來
(function(food){
console.log('大俠愛吃' + food)
}('漢堡包'));
執行結果
這樣就不會報錯了,這也是IIFE,也是最常在JS框架、套件看到的寫法。
至於放在後面,立即呼叫的()
,要放在裡面或外面都可以。
(function(food){
console.log('大俠愛吃' + food)
})('漢堡包');
這樣可以,兩個結果都一樣,不過作者認為開發者擇一使用就好,因為要保持撰寫風格的一致。
另外,ES5版本的JS有全域執行環境(作用域)、函式執行環境(作用域)兩種,直到ES6版才出現塊級作用域,在ES6出來前,為了避免設定太多的全域變數,開發者往往會將變數設定在函式中,使其成為區域變數,尤其是設定在IIFE中,確保不會汙染到全域環境的變數。
例如
var seafood = '波士頓龍蝦';
(function(name){
var seafood = '北海道帝王蟹';
console.log(name + '好~想吃' + seafood);
}('Simon'));
結果是
因為執行環境的不同,'北海道帝王蟹'只存在IIFE裡,不會影響到外部環境的變數。
也就是說,我們可以將程式碼包在被()
包住的IIFE裡,當IIFE和全域環境有宣告相同變數名稱時,IIFE不會蓋掉全域的變數。
不少JS框架、套件的開頭與結尾被()
包住,程式碼被立即函式包著,其目的是怕污染到使用者(開發者)的全域環境。
反過來說,若全域和IIFE內都有重複的變數名,那這些框架、套件該如何取用全域的變數呢?
var food = '水牛城雞翅';
console.log(food);
(function(global){
var food = '雞塊';
global.food = '麥脆雞';
console.log(food);
})(window);
console.log(food);
為避免無意義的外部查找,將window當參數傳入,使其成為這個IIFE的區域物件,確保在IIFE內的程式能故意取用到全域的特定變數。
小結
使用立即函式的好處,除了可以立即執行程式碼,省略多餘的呼叫,還可以用來避免汙染全域執行環境的東西,減少開發時因相同命名相衝的bug。
今天的筆記內容可以參照Udemy課程:JavaScript 全攻略:克服JS的奇怪部分4-44、4-45
謝謝,解釋的非常清楚,一直在思考匿名函數外部在做一個()的用途是什麼,原來就只是把它當作IIFE。此外IIFE的函數放在函數最後端也造成一時的理解障礙,但看完版大的文章後就非常清楚了解這個函數的用途。
不過我個人認為JS強大的地方在於它是函數式程式語言,不知道ES6出現塊級作用域對於這個語言本身的理念是否有影響。