JavaScript 核心觀念(36)-函式以及 This 的運作-立即函式

前言

關於立即函式是一個非常常見的技巧,在許多框架上都可以看到的技巧。

立即函式

立即函式是什麼呢?立即函式全名為「immediately-invoked function expression」也就是所謂的「IIFE」,完整的中文名稱為「立即呼叫函式表達式」,而這是什麼意思呢?在前一章節我們有了解到具名陳述式是必須被呼叫才會執行的

1
2
3
4
5
function fn() {
console.log('Ray');
}

fn();

而 IIFE 則不用透過我們的呼叫,在程式碼運行時,就會自己執行。

1
2
3
(function() {
console.log('Ray');
})();

IIFE 的關鍵在於最尾段的括號 (),通常可能你會看到兩種撰寫方式

1
2
3
4
5
6
7
(function() {
console.log('Ray');
})();

(function() {
console.log('Ray');
}());

這兩種方式並沒有絕對的答案,因為都是可以正常執行 IIFE,在這邊我們可以看到 IIFE 並不具有名稱,也就是所謂的匿名函式的一種,但與前面所看到的匿名表達式又不同,一般的匿名表達式是必須被呼叫才會執行裡面結果

1
2
3
4
5
var fn = function() {
console.log('Ray');
}

fn();

這邊可能會有人疑惑說以下的寫法算是什麼?

1
2
3
(function fn() {
console.log('Ray');
}());

其實也是 IIFE 的一種,但 function 的名稱其實是不用撰寫的,因為沒有必要,但 JavaScript 不會提示你不用寫。

因此 IIFE 第一個特點這邊就可以很明顯感受出來,也就是立即執行函式(廢話),但 IIFE 還有另一個特點,也就是當它執行完畢之後,你就無法再次呼叫甚至是外層呼叫,而且宣告在裡面的變數,只有 IIFE 內可以取用

1
2
3
4
5
6
7
(function fn() {
console.log('Ray');
var a = 'Hello';
}());

fn(); // fn is not defined
console.log(a); // a is not defined

因此我們這邊可以透過 MDN 了解關於 IIFE 的說明

IIFE (Immediately Invoked Function Expression) 是一個定義完馬上就執行的 JavaScript function。

除此之外,這邊還有一個重點要注意

這樣的寫法可以避免裡面的變數污染到 global scope。

這是什麼意思呢?IIFE 最大重點觀念在於認清作用域,在還沒有 block 作用域,也就是 ES6 之前,其實非常容易發生所謂的污染問題,因為 var 宣告的變數是基於 function 作為作用域,因此以下程式碼就非常容易污染到 Window

1
2
3
4
for(var i = 0; i < 10; i++) {
console.log(i);
}
console.log(i); // 10

因此這種狀況我們就必須去避免開發時污染到 Window,這時候就可以使用 IIFE 去侷限作用域,讓變數 i 只活在 IIFE 內

1
2
3
4
5
6
7
(function() {
for(var i = 0; i < 10; i++) {
console.log(i); // 0~9
}
console.log(i); // 10
})();
console.log(i); // i is not defined

那這邊你可能會問,對外無法取得 IIFE 內部的變數與函式,那可以對內傳遞參數嗎?答案是可以的,寫法則是如下

1
2
3
(function(name) {
console.log(name); // Ray
})('Ray');

而且本身因為 IIFE 就是一個表達式,所以也可以利用 return 並搭配一個變數來接收 IIFE

1
2
3
4
5
6
var name = (function(name) {
console.log(name); // Ray
return name;
})('Ray');

console.log(name); // Ray

最後 IIFE 有一個雷點很常發生也就是「;」,如果沒有正確撰寫 ; 就會出現「Uncaught TypeError: (intermediate value)(...) is not a function」的錯誤

1
2
3
4
5
6
(function(name) {
console.log(name); // Ray
})('Ray')
(function(name) {
console.log(name); // Ray
})('Ray')

而該錯誤的解法就是在括號前面補上 ; 即可

1
2
3
4
5
6
(function(name) {
console.log(name); // Ray
})('Ray')
;(function(name) {
console.log(name); // Ray
})('Ray')

這邊主要的錯誤原因與很前面的章節「JavaScript 核心觀念(12)-運算子、型別與文法-ASI 自動插入分號」有關係。

那麼最後來講一下為什麼要使用 IIFE,IIFE 最主要目的是避免污染到全域執行環境並照成污染與衝突,因此在以下範例程式碼,雖然變數是建立在函式內,但 function 卻是直接建立在全域執行環境下

1
2
3
4
function getName() {
console.log('Ray');
}
getName();

因此這時候若有一個類似同名的變數或是函式就會發生衝突。

雖然現在 ES6 出了 letconst,但使用變數宣告的記憶體是無法被刪除的,因此當宣告越來越多時,效能自然就會比較差,在此 IIFE 還是有它一定的功用。

參考文獻

Liker 讚賞

這篇文章如果對你有幫助,你可以花 30 秒登入 LikeCoin 並點擊下方拍手按鈕(最多五下)免費支持與牡蠣鼓勵我。
或者你可以也可以請我「喝一杯咖啡(Donate)」。

Buy Me A Coffee Buy Me A Coffee

Google AD

撰寫一篇文章其實真的很花時間,如果你願意「關閉 Adblock (廣告阻擋器)」來支持我的話,我會非常感謝你 ヽ(・∀・)ノ