JavaScript 立即调用函式 IIFE (Immediately Invoked Function Expression) 是什么?优缺点是什么?

2023年2月9日

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

立即调用函式 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 上追蹤我們