概念
什么是立即执行函数,顾名思义在声明一个函数之后,立马执行它。这个函数一般是匿名函数。
作用
立即执行函数的实质就是想创建一个独立的作用域,这个作用域里的变量,外面访问不到。1
2
3
4
5function xxx(){
var a=1
console.log(a)
}
xxx()
如果把这个变量放到函数里,不就行了吗?但这样存在的问题是,虽然你隐藏了一个变量,但你又暴露了一个全局变量xxx。
其实,我们只要把这个函数变成匿名函数就行了。1
2
3
4function (){
var a=1
console.log(a)
}.call()
这样就可以让一个匿名函数立马执行,而函数里变量也是局部变量。但是,这样写浏览器会报错,认为这种语法不对。这时可以在函数加一个转换符:~、!、-、+和括号。一般我们约定都用!。如果用-、+和括号,可能会产生bug。因为这三个操作符是向前看的,可能它们前面还有其它的代码,会造成不必要的错误。
1 | !function (){ |
此时,控制台打出两个值,分别是1和true。我们知道,在一个函数在没写返回值时,该函数默认的return是undefined,而!又是取反,所以控制台会打出true。对于这个立即执行函数,其实我们并不关心其匿名函数的返回值。
举例分析
这是一道经典的面试题。假如HTML页面里有五个li标签。1
2
3
4
5
6var liList = document.querySelectorAll('li')
for(var i=0;i<liList.length;i++){
liList[i].onclick = function (){
console.log(i)
}
}
但我们依次点击这五个li时,控制台会依次打出5个5。为什么不是依次打出0,1,2,3,4呢?
因为用户的点击一定在for循环结束后。for循环结束后,i已经变为了5。因为每个函数的作用域中,引用的都是同一个变量i。那么你点击时自然会打出5个5。
如何解决这个问题呢?我们可以用立即执行函数,给每个li创造一个独立的作用域即可。因为点击事件是异步的。1
2
3
4
5
6var liList = document.querySelectorAll('li')
for(var i=0;i<liList.length;i++){
!function(ii){
liList[ii].onclick = function(){console.log(ii)}
}.call(undefined,i)
}
在for循环执行时,立即执行函数便会执行。这里的匿名函数有一个参数ii,也就是最终函数要返回的值。在调用每个匿名函数时,我们传入了变量i。由于函数参数是按值传递的,所以就会将变量i的当前值赋值给参数ii。此后,ii的值是一直不变的。for循环里,i的值从0变到4,对应5个立即执行函数,这5个立即执行函数里的li分别是0,1,2,3,4。
ES6新语法
我们知道立即执行函数就是想搞个块级作用域,不让外面访问这里面。ES6语法中,新出了一个let变量声明,这个let声明的变量是块级作用域里的变量。JS中,花括号可以表示为块级作用域。1
2
3
4{
var a =1
console.log(a)
}
这种写法可以代替立即执行函数,只不过以前的很多代码都是写的立即执行函数,所以立即执行函数也要了解。