3.6 立即执行函数
Immediately-Invoked Function Expression (IIFE)
参考自此篇文章
现在我们定义了一个函数function foo(){}
或者var foo = function(){}
,函数名
后加上一对小括号即可完成对该函数的调用
,比如下面的代码:
var foo = function() {
/* code */
}
foo()
接着我们来看下面的代码:
function(){ /* code */ }(); // SyntaxError: Unexpected token (
- 报错了,这是为何?
这是因为在 javascript 代码解释时,当遇到function
关键字时,会默认把它当做是一个函数声明
,而不是函数表达式,如果没有把它显视地表达成函数表达式,就报错了,因为函数声明需要一个函数名
,而上面的代码中函数没有函数名。以上代码在执行到第一个左括号时报错,理论上是应该有个函数名的。
有意思的是,如果我们给它函数名,然后加上()
立即调用,同样也会报错,而这次报错原因却不相同:
function foo(){ /* code */ }(); // SyntaxError: Unexpected token )
- 为什么会这样?
在一个表达式
后面加上括号,表示该表达式立即执行
;
而如果是在一个语句
后面加上括号,该括号完全和之前的语句不相关
,而只是一个分组操作符
,用来控制运算中的优先级(小括号里的先运算)。所以以上代码等价于:
function foo(){ /* code */ }
(); // SyntaxError: Unexpected token )
相当于先声明了一个叫 foo 的函数,之后进行()内的表达式运算,但是()(分组操作符)内的表达式不能为空,所以报错。(以上代码,也就是执行到右括号时,发现表达式为空,所以报错)。
- 如何解决?
其实很简单,只需要用括号全部括起来即可,比如下面这样:
(function() {
/* code */
})()
- 为什么这样就能立即执行并且不报错呢?
因为在 javascript 里,括号内部不能包含语句,当解析器对代码进行解释的时候,先碰到了()
,然后碰到function
关键字就会自动将()
里面的代码识别为函数表达式
而不是函数声明。
而立即执行函数并非只有上面的一种写法,写法真是五花八门:
// 最常用的两种写法
(function() {
/* code */
})() // 老道推荐写法
(function() {
/* code */
})() // 当然这种也可以
// 括号和JS的一些操作符(如 = && || ,等)可以在函数表达式和函数声明上消除歧义
// 如下代码中,解析器已经知道一个是表达式了,于是也会把另一个默认为表达式
// 但是两者交换则会报错
var i = (function() {
return 10
})()
true &&
(function() {
/* code */
})()
0,
(function() {
/* code */
})()
// 如果你不怕代码晦涩难读,也可以选择一元运算符
!(function() {
/* code */
})()
~(function() {
/* code */
})()
-(function() {
/* code */
})()
+(function() {
/* code */
})()
// 你也可以这样
new function() {
/* code */
}()
new function() {
/* code */
}() // 带参数
3.6.1 为什么要使用 IIFE
-
不必为函数命名,避免了污染全局变量;
-
IIFE内部形成了一个单独的作用域,可以封装一些外部无法读取的私有变量。