JavaScript 立即調用函式 IIFE (Immediately Invoked Function Expression) 是什麼?優缺點是什麼?

2023年2月9日

💎 加入 E+ 成長計畫 與超過 300+ 位軟體工程師一同在社群中成長,並且獲得更多的軟體工程學習資源

立即調用函式 IIFE (Immediately Invoked Function Expression) 是什麼?

JavaScript 中的立即調用函式 (IIFE,Immediately Invoked Function Expression),指的是一種在定義時立即執行的匿名函式,通常用於創建一個局部作用域,避免全局污染。

語法格式如下:

(function () {
  // Code to be executed
})();

在這種表達式中,函式定義與函式調用的括號是一起的,確保函式只會被執行一次,而不是定義後可以多次執行。在這個封閉的作用域中,變量和函式都不會污染全局環境。

立即調用函式的好處

創建局部作用域

通過使用 IIFE 可以創建一個局部作用域,避免全局變量的污染。以下程式碼可以看到,在 IIFE 中,有一個局部變量 localVariable。localVariable 只能在 IIFE 內訪問,不能在 IIFE 外訪問,

// Global scope
var globalVariable = "global variable";

(function () {
  // Local scope inside IIFE
  var localVariable = "local variable";
  console.log(localVariable); // local variable
})();

console.log(localVariable); // ReferenceError: localVariable is not defined
console.log(globalVariable); // global variable

避免命名衝突

IIFE 可以為變量創建了一個單獨的命名空間,避免函式名和變量名的衝突。

// Global scope
var globalVariable = "global variable";

(function () {
  // Local scope inside IIFE
  var globalVariable = "local variable inside IIFE";
  console.log(globalVariable); // local variable inside the IIFE
})();

console.log(globalVariable); // global variable

模組化編程

IIFE 可以將程式碼分為獨立的模組,方便了程式碼的管理和維護。在《什麼是前端模組化?》這篇文章中曾經有提過,在前端模組化開始發展、並且還沒有前端模組化工具時,一開始就是使用 IIFE 作為模組化的實踐方式。

在下方程式碼例子中,我們通過兩個 IIFE 分別創建了兩個模組:module1 和 module2。每個模組內部都有自己的變量和函式,而 IIFE 的作用是創建局部作用域,避免了變量的污染。接著,我們在 window 添加對應的模組對象,實現了對模組的公開。使用時,我們可以直接通過 window 對象訪問模組中的函式,實現了模組化編程。

// Module1
(function () {
  var module1Variable = "I am a variable in module 1";
  function module1Function() {
    console.log(module1Variable);
  }
  window.module1 = {
    module1Function: module1Function,
  };
})();

// Module2
(function () {
  var module2Variable = "I am a variable in module 2";
  function module2Function() {
    console.log(module2Variable);
  }
  window.module2 = {
    module2Function: module2Function,
  };
})();

// Usage
module1.module1Function(); // I am a variable in module 1
module2.module2Function(); // I am a variable in module 2

提高程式碼執行效率

IIFE 可以在定義時立即執行,避免了函式的不必要的存儲和調用,提高了程式碼的執行效率。

IIFE 的缺點

儘管上述提到許多 IIFE(Immediately Invoked Function Expression)的優點,但也存在一些缺點,例如

  • 程式碼不易維護:當程式碼變得更加複雜時,IIFE 的程式碼容易變得龐大,不易於維護和閱讀。
  • 不利於重複使用:IIFE 的程式碼通常是一次性的,無法復用,因此在需要多次調用時不太方便。
  • 增加程式碼複雜度:使用 IIFE 可能會使程式碼變得更加複雜,特別是當程式碼量很大時。

經典面試題 - setTimeout、Scope、IIFE

以下為一題有關 setTimeout、Scope、IIFE 的經典面試題,請試著回答

以下輸出結果為何?

for (var i = 1; i <= 5; i++) {
  setTimeout(function () {
    console.log(i);
  }, 0);
}

解答

// 立即輸出五個 6
6;
6;
6;
6;
6;

原因

這是因為在 JavaScript 中,var 聲明的變量是函式作用域 (function scope),而不是塊級作用域 (block scope)。因此,在循環結束後,i 的值為 6,每個 setTimeout 回調函式引用的都是同一個 i 變量,因此輸出的結果都是 6。

延伸閱讀:Javascript 的作用域 (Scope) 與作用域鏈 (Scope Chain) 是什麼?

解決方法

要如何有辦法,每隔一秒輸出一個 1、2、3、4、5 呢?

  1. IIFE

    這裡使用立即調用函式(IIFE)和匿名函式形成一個私有作用域(相當於閉包),私有作用域中的變量和全局作用域中的變量互不衝突;這時每次 for 循環傳入的 i 的值都將作為私有變量被保存在內存中,等待 for 循環執行完畢後,跟隨任務隊列輸出。

    for (var i = 1; i <= 5; i++) {
      (function (i) {
        setTimeout(function () {
          console.log(i);
        }, i * 1000);
      })(i);
    }
    
  2. 使用 let 聲明變量

    在 ES6 之後,我們就可以使用 let 來解決這個問題,因為 let 聲明的變量是塊級作用域 (block scope),所以可以通過在循環內部使用 let 聲明 i 變量來解決問題。


相關文章

🧵 如果你想收到最即時的內容更新,可以在 FacebookInstagram 上追蹤我們