为什么IIFE语法需要小括号包裹

IIFE 的语法

1
2
3
4
5
6
7
8
(function say() {
console.log('hello')
})()

// or
(function() {
console.log('hello')
})()

我们经常使用上面的语法来创建一个代码块,好处是能够将变量作用域保存在自执行函数中,不污染外部的变量,那么,为什么下面的语法会报错呢?

1
2
3
function say() {
console.log('hello')
}()

要弄清楚为什么报错,我们必须理解函数表达式和函数声明的区别

函数声明(Function Declaration)和 函数表达式 (Function Expression)

最显而易见的区别,是两者定义函数方式的区别:

函数声明

1
function say() {}

函数表达式

1
2
3
const say = function() {}
// or
const say = function say() {}

关于函数声明和函数表达式的更详细区别,不在本文赘述

为什么函数声明后面加一个小括号会报错?

答案很简单,调用一个使用函数声明定义的函数,必须使用函数名

1
2
3
4
5
6
7
function say(){}
say()

// not work
function say() {

}()

而用一对括号 Group Operator 包裹住函数,实际上是把函数声明转换成了函数表达式,而函数表达式可以有函数名也可以匿名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
(function say() {

})()

(function() {

})()

// 可以拆成两步理解
// 1. 用括号包住函数,Group Operator 会进行一次求值 (evaluated),会返回一个变量
var say = (function say(){

})
// 2. 对该变量进行函数调用
say()

// 省去变量赋值相当于
(function say() {

})()

PS:() 操作符只能对表达式求值,所以被括号包裹住的函数是函数表达式定义的函数,这也是为什么这种自执行函数被叫做 IIFE(Immediately Invoked Function Expression),立即执行函数表达式

思考

那么,是不是只要想办法让解析器在遇到函数声明的时候能够进行一次求值转化为函数表达式,就能正常运行呢?

解决方法:

1
2
3
4
5
6
7
0, function say(){}() 

!function say(){}()

+function say(){}()

// ...

只要能让该函数出现在的位置能被解析器当作表达式进行一次求值(evaluated)即可

参考:
Explain the encapsulated anonymous function syntax
JavaScript 匿名函数有哪几种执行方式?