谈一谈匿名函数立即执行(IIFE)的写法
今天在写setTimeout
时,遇到了匿名函数执行的问题,记录研究一下。
写法
匿名函数立即执行,IIFE (Immediateoly-Invoked Function Expressions),基本上差不多,都这样:
(function(){
console.log('a IIFE ?')
})();
写法,还有很多种:
!function(){
console.log('a IIFE ?')
}();
new function(){
console.log('a IIFE ?')
}();
+function(){
console.log('a IIFE ?')
}();
void function(){
console.log('a IIFE ?')
}();
(function(){
console.log('a IIFE ?')
}());
分号
对于末尾的;
分号,最好还是顺手加上,因为如果不加,遇到两个都是用括号()
包裹执行的IIFE时,就会遇到问题,比如:
(function(){
console.log('a')
})()
(function(){
console.log('b')
})()
只有a
可以显示,b
会报错(intermediate value)(...) is not a function
。
这里有个灵活的地方,如果上面的b
函数括号位置修改一下,改为放在内容:
(function(){
console.log('a')
})()
(function(){
console.log('b')
}())
那么a,b
都可以显示,但也会报同样的错误,稍后再做分析。
不加分号,上面的内容会被JS理解为:
(function(){
console.log('a')
})()(function(){
console.log('b')
})()
也就是说,a
匿名函数执行后,没有;
的隔断,后面的b
立即执行函数与a
合成了一个函数。执行到输出完a
时,没有reutrn
,后面的()
相当于对undefined
进行了执行,所以报错。
依然不加;
,我把上面的内容修改一下就看明白了:
(function(){
return function(){
console.log('a')
}
})()
(function(){
console.log('b')
}())
这个执行结果是:
b
a
为什么是这样呢,而不是顺序输出?
首先来看看哪里做了修改:把a
函数增加了一个return
,也就是说,在执行完a
函数部分时,得到的并不是输出内容,而是返回了一个新的匿名函数:
function(){
console.log('a')
}
在此基础上,执行完b
函数,先得到输出b
。
好了,到了关键的地方,为什么接着输出了a
?
先说结果:
(function(){
console.log('b')
}())
这部分执行完,对前面新返回的匿名函数进行了执行,于是得到a
。
来举个例子,为什么能执行:
+function(){
console.log('i can be executed');
}('nothing here')
不仅是()
本身可以执行函数,在其中增加内容也是没有任何影响,依然发挥执行函数的作用。
回到前面的例子,b
函数部分
(function(){
console.log('b')
}())
相当于在括号内部进行了匿名函数执行,输出b
之后,又发挥了()
执行函数的功能,使得前半部return
得到的匿名函数得以执行,最终输出了a
。
说了这么多,还是要记得写;
才是最重要的,就可以避免出现这种错误。
something cool?
为了试验分析,我尝试了好多种写法,发现几种有趣的:
!function(){
return function(){
return function(){
return function(){
return function(){
return function(){
return function(){
return function(){
console.log('so cool')
}
}
}
}
}
}
}
}()()()()()()()();
(()=>()=>()=>()=>()=>()=>console.log('super cool'))()()()()()();
小细节
1.使用ES6箭头函数执行IIFE时,需要使用()
包裹才可以,! + - void new
等方法不可以。
比如:
()=> console.log(666)()
//这样没反应
!()=> console.log(666)()
//直接报错 Unexpected token )
(()=> console.log(666))();
//带()才行
2.前面那个分号的例子中,如果观察仔细,你会发现:
(function(){
return function(){
console.log('a')
}
})()
(function(){
console.log('b')
}())
//这里值得注意
如果换成:
(function(){
return function(){
console.log('a')
}
})()
('cool')
没问题,都不会报错。
但是不换成cool的话,()
内部的匿名函数:
function(){
console.log('b')
}()
是正常执行了,可要是把这个写法原封不动,执行一下可是要报错的,因为没有这样的IIFE的写法。至于为什么,我也不太清楚,未来深入学习的时候会留心观察的。
END