立即函式 IIFE

Vicky
宅宅薇琪 [前端學習筆記]
6 min readFeb 12, 2021

IIFE 全名為Immediately Invoked Functions Expressions,指的是可以立即執行的Functions Expressions函式表達式。但 IIFE 最主要目的是避免污染到全域執行環境並照成污染與衝突,本篇將會介紹 IIFE 是如何執行以致於不會汙染到全域環境。

立即函式 IIFE 介紹

  1. 不需要呼叫此函式,也能執行 (立刻執行)
  2. IIFE 最主要目的是避免污染到全域執行環境並照成污染與衝突
  • 限制作用域的用途,「立即函式」無法在外層呼叫
  • 變數的作用域只在函式內,在外層無法取得變數

3. 「立即函式」本身是表達式,所以也能回傳一個值

以下列舉「立即函式」的特點

透過「具名函式」的方式來執行的 IIFE,且 IIFE 無法在外層被呼叫

(function IIFE() {    
console.log('立即函式', IIFE);
}());
console.log(IIFE); // IIFE is not defined (無法在函式外被再次執行)
IIFE; // IIFE is not defined (「立即函式」無法在外層被呼叫)

IIFE 也是可自我執行的「匿名函式」

(function () {
console.log('立即函式'); // 立即函式
}());

小括號的位置也可移至外層

(function () {
console.log('立即函式'); // 立即函式
})();

變數只活在 IIFE 內

「立即函式」可以避免裡面的變數污染到 global scope。

變數 Ming 宣告在「立即函式」的內層,只能在「立即函式」內被取得,在外層無法取得變數。

(function() {
var Ming = '小明'; // 透過「立即函式」限制變數的作用域
console.log(Ming); // 小明
})();
console.log(Ming); // Ming is not defined (在外層無法取得變數)

限制作用域的用途

就是為了避免命名衝突,而範例中 getName 是屬於全域的函式。因此在開發中,getName 還是可能與其它變數名稱衝突,如果透過立即函式,就連同函式宣告都能限制作用域。

function getName() {
var Ming = ‘小明’;
console.log(Ming); // (限制變數的作用域)
};
getName(); // 小明
console.log(Ming); // Ming is not defined

「立即函式」也能傳遞參數

「立即函式」本身是表達式,所以也能回傳一個值

可以利用 return 並搭配一個變數來接收 IIFE

var whereMing = (function (where) {
console.log(where);
return where; // return 會把值傳出來外層,外層變數就可接收此函式
})('小明在這'); // 透過 "小括號" 把參數往前傳
console.log(whereMing);
// 小明在這 (變數 whereMing 接收此函式的 return 結果)

return用法 :

  • 只要你需要將結果回傳時,就會使用到該方式。
  • 這邊主要觀念與執行堆疊的記憶體釋放有關係,當函式執行完畢後就會釋放記憶體,所以若要保留結果,那麼就必須 return。

「立即函式」不符合 ASI 規則,無法自動插入分號

以下兩個「立即函式」因沒有使用分號隔開被視為同一行,
「立即函式」不符合 ASI 規則,無法自動插入分號。

(function(){
})() // 跳錯,(intermediate value)(...) is not a function
(function(){
})() // 這個括號的內容不是 function

解決方法 : 在使用任何「立即函式」的前方或後方要補上分號,才能正確執行。

(function(){
})();
(function(){
})()

「立即函式」傳遞變數的手法

把前一個「立即函式」的內容傳至下一個「立即函式」內

使用物件 “傳參考” 的特性傳遞

var a = {};   // 先宣告一個物件,物件有傳參考的特性
(function(b){
b.person = '小明';
console.log(b === a); // true
console.log(b.person === a.person); // true
})(a);
(function(c){
console.log(c.person); // 小明
console.log(c === a); // true
console.log(c.person === a.person); // true
})(a)
  1. function(b) 和 function(c) 裡面的 b 和 c 參數,都是傳進來 a 的值。
    如果將 a 與 b 和 c 比較會發現都為 true。 a 與參數 b、c 的值是一樣的。
  2. 有一個 a 的物件帶到 b,又因為物件是傳參考,所以 a 和 b 的參考路徑都是相同,所以 b.person=小明; 實際上因參考路徑相同所以 a、b 底下都有一個屬性 person 跟值小明,第二個立即函式也接收了 a 的參考路徑故 console.log(c.person); //小明 。 a、b、c的參考路徑都是相同。

將參數掛載到 window 全域物件的方式傳遞

將 window 傳入,可以確保框架正確的掛載到全域變數 window 上

(function(global) {     
global.person = '小明';
})(window) // window 是全域物件,把 window 全域物件傳到前面這個「立即函式」
;(function(c) {
console.log(person); // 小明
})()
  1. 將傳進第一個立即函式的參數改為全域物件 window。第二個立即函式印出全域物件的屬性 person。
  2. window 是一個物件,而透過 global 傳入後也是一個物件傳參考特性,因此 global.person 其實就是 window.person。
  3. window 是全域物件,把 window 全域物件傳到前面的立即函式,在全域物件掛上屬性 person,並把值傳到另一個立即函式。
var greeting = 'Hola';
(function(global, name){
var greeting = 'Hello';
global.greeting = 'Hello' // 讓 Hola 變成 Hello
console.log(greeting+' '+name) // Hello Iris
})(window, 'Iris') // 取用全域中的變數,並代入 IIFE 中來更改全域變數

console.log(greeting) // Hello

--

--