0%

javascript-IIFE

介绍

IIFE(Immediately Invoked Function Expression)中文名称:立即执行函数表达式,是一种JavaScript编程模式,它允许函数在定义后立即执行。由这个定义可以看出IIFE有两个特点:

  1. 它是函数表达式
  2. 它是定义后立即执行的

来看一个例子:

1
2
3
(function() {
console.log('I am an IIFE');
})();

将这段代码放在浏览器控制台中执行,会输出I am an IIFE。这就是一个简单的IIFE。

分析一下上面的代码,它分为两部分,每个部分由一组()包裹。
第一部分是一个函数(本例用的是匿名函数,也可以是具名函数):

1
2
3
(function() {
console.log('I am an IIFE');
})

第二部分是一个函数调用

1
();

这两部分结合在一起,就形成了一个IIFE。

快速掌握IIFE

IIFE的语法其实不复杂,但是刚开始要记住它并不容易,这里教大家一个小技巧,可以快速书写IIFE代码。

  1. 首先,写下两组括弧()()
  2. 然后在第一组括弧内写下函数定义 - 注意这里必须是匿名函数。
  3. 如果函数需要参数,那么在第二组括弧内传入参数。

一道练习题,写一个IIFE,计算1 + 2的和

我们使用上面的技巧来书写:

首先写下两组括弧

1
()()

然后在第一组括弧内写下函数定义

1
2
3
(function add(a, b) {
console.log(a + b);
})()

因为这里的add函数需要参数,所以我们要在第二组括弧内传入参数

1
2
3
(function add(a, b) {
console.log(a + b);
})(1, 2);

IIFE的变种

上面介绍的IIFE写法是最常见的形式,其实IIFE还有一些其他写法(至于为什么这些写法是可以的,我们下一篇再做介绍),现罗列如下:

这种写法将调用函数的括号放到了第一组括弧内部。

1
2
3
(function() {
console.log('I am an IIFE');
}());

以下几种写法都是使用一元运算符来实现的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
!function() {
console.log("IIFE with !")
}();

~function() {
console.log("IIFE with ~")
}();

+function() {
console.log("IIFE with +")
}();

-function() {
console.log("IIFE with -")
}();

这些一元运算符的作用是将函数定义转换为一个表达式,从而使其可以被调用。如果不加这些一元运算符的话,就变成了下面的形式:

1
2
3
function() {
console.log("IIFE without unary operator")
}();

这会导致一个语法错误,因为JavaScript引擎会将其解析为一个函数声明,而不是函数表达式。函数声明需要一个函数名,而匿名函数没有名字,所以会报错:

1
Uncaught SyntaxError: Function statements require a function name

我们给它加上一个名字,变成下面这样

1
2
3
function foo() {
console.log("IIFE with function name")
}();

但是还是会报错:

1
Uncaught SyntaxError: Unexpected token ')'

javascript引擎会将上述代码解析为一个函数声明和一个分组操作符 - 即(),但是分组操作符内不能是空的,必须有一个表达式,我们在里面加上一个1

1
2
3
function foo() {
console.log("IIFE with function name")
}(1);

这样就不会报错了,但是这不是一个IIFE了。它的返回值就是分组表达式的值,也就是1。

为什么需要IIFE?

早期在JavaScript尚未支持模块化的时候,IIFE是实现模块化的一种简单方式,它可以创建一个独立的作用域,从而避免全局污染,下面看一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const myModule = (function MyModule() {
const privateVariable = 'private variable';
const publicVariable = 'public variable';

function privateMethod() {
console.log(privateVariable);
}

function publicMethod() {
console.log(publicVariable);
}

// 将需要暴露给外界的变量和方法写在返回的对象中
return {
publicVariable,
publicMethod,
};
})();

console.log(myModule.publicVariable); // 访问公共变量
myModule.publicMethod(); // 访问公共方法

console.log(myModule.privateVariable); // undefined
myModule.privateMethod(); // TypeError: myModule.privateMethod is not a function, privateMethod is not accessible

分析一下上面的代码,这是一个典型的IIFE + 闭包实现模块化的例子。

MyModule是一个IIFE,它在内部定义了:

  • 私有变量privateVariable
  • 私有方法privateMethod

这些变量和方法无法从外部访问。

它还定义了

  • 一个公共变量publicVariable
  • 一个公共方法publicMethod

这些可以通过返回的对象访问。

IIFE的返回值赋值给了myModule变量,这样就可以通过myModule访问公共变量和方法。

注意:这个例子仅供参考,在ES6模块已经十分普及的今天,这种方式已经不推荐使用了。