1. 写法
立即执行函数的英文名字叫做: immediately-inovked-function expression, 简称 IIFE. 基本形式 ^^函数表达式 + () = IIFE^^ 所以出现了如下这些立即执行函数的方法:
(function(){alert('我是匿名函数')} ()) // 用括号把整个表达式包起来
(function(){alert('我是匿名函数')}) () // 用括号把函数包起来
!function(){alert('我是匿名函数')}() // 求反,我们不在意值是多少,只想通过语法检查。
+function(){alert('我是匿名函数')}()
-function(){alert('我是匿名函数')}()
~function(){alert('我是匿名函数')}()
void function(){alert('我是匿名函数')}()
new function(){alert('我是匿名函数')}()
我是这样子来认识这个问题的:
当JS引擎在解析代码的时候,碰到function就认为你是函数声明.但是找不到必要的函数名, 那就回出现挂载不到内存的情况.所以会直接报错,如function(){}()这个样子.
报错提示语: Function statements require a function name
而在匿名函数外面加上()之后,JS引擎将这玩意儿认知为一个整体. 直接在堆内存开辟一个空间放它.
同理, 匿名函数前面加上 ! + - ~就是让JS引擎将认为匿名函数().比如const a = 1 + function()(return 3)()中的a为4
越写越觉得不对. 这部分的理解可能需要我了解了JS引擎编译原理之后才能够解决的问题了. 这一段的疑惑暂时存在脑海中.
W3C推荐的写法: (function(){}()).
2. 有什么用
🚩: 创建私有作用域 就是避免变量污染.典型的场景如下:
const LiDiv = ul.getElementsByTagName('li')
for (var i = 0; i < 5; i++) {
LiDiv[i].onclick = function() {
console.log(i)
}
}
打印出都是5. for循环结束之后, 用户可以有进行操作的时候,i已经是5了.
而前面的LiDiv[i]. 是可以正确的渠道LiDiv中对应的div的.是因为它是一个同步任务. 而用户点击这种IO操作明显就是一个宏任务.
还有这么一个典型的考题:
for (var i = 0; i < 5; i++) {
setTimeout(() => {
console.log(i)
}, 1000 * i)
}
执行结果就是,在一毫秒之后开始输入5,之后大概是每隔1000ms执行一次5.
上面的1000 * i能够执行预想的0ms, 1000ms, 2000ms, 3000ms, 4000ms.也是因为它在把setTimeout任务丢入宏任务队列的时候是同步任务. 这里涉及到了 事件循环的问题.
要解决上面的这两个问题很简单. 既然变量污染了,就让变量不污染.那么IIFE就是这个作用的.
for(var i = 0; i < 5; i++){
!function(j){
liList[ii].onclick = function() {
console.log(i) // 0、1、2、3、4
}
}(i)
}
for (var i = 0; i < 5; i++) {
;(function(i){
setTimeout(() => {
console.log(i)
}, 1000 * i)
})(i)
}
3. 一道似是而非的题目
;(function fun() {
fun = 2
console.log(fun)
})()
上面的输出是什么?
[Function fun]
原因:
-
具名函数表达式的名称是不能修改的
-
非严格模式会静默处理,严格模式会报错