立即执行函数表达式
立即执行函数表达式,大部分人也称为自执行函数。
自执行函数的写法
匿名函数
(function() {console.log(2)})()
具名函数
(function log() {console.log(2)})
自执行函数的传参
(function add(a, b) {console.log(a + b)})(1, 2)
返回值
let fn = (function add(a, b) {return a + b;}) (2, 4)console.log(fn)
自执行函数也可以传递函数作为参数
var a =2;(function log(fn) {fn(window)})(function fn(global) {var a = 3;console.log(a); // 3console.log(global.a); // 2})
立即执行函数有什么用?
只有一个作用:创建一个独立的作用域。
这个作用域里面的变量,外面访问不到(即避免「变量污染」)。
以一个著名的面试题为例:
var liList = ul.getElementsByTagName('li')for(var i=0; i<6; i++){liList[i].onclick = function(){alert(i) // 为什么 alert 出来的总是 6,而不是 0、1、2、3、4、5}}
为什么 alert 的总是 6 呢,因为 i 是贯穿整个作用域的,而不是给每个 li 分配了一个 i,如下:
那么怎么解决这个问题呢?用立即执行函数给每个 li 创造一个独立作用域即可(当然还有其他办法):
var liList = ul.getElementsByTagName('li')for(var i=0; i<6; i++){!function(ii){liList[ii].onclick = function(){alert(ii) // 0、1、2、3、4、5}}(i)}
在立即执行函数执行的时候,i 的值被赋值给 ii,此后 ii 的值一直不变。
i 的值从 0 变化到 5,对应 6 个立即执行函数,这 6 个立即执行函数里面的 ii 「分别」是 0、1、2、3、4、5。
为什么还要用另一对括号把匿名函数包起来呢?
其实是为了兼容 JS 的语法。
如果我们不加另一对括号,直接写成👇
function (){alert('我是匿名函数')}()function a(){alert('我是匿名函数')}()
浏览器会报语法错误。想要通过浏览器的语法检查,必须加点小东西,比如下面几种
(function(){alert('我是匿名函数')} ()) // 用括号把整个表达式包起来(function(){alert('我是匿名函数')}) () //用括号把函数包起来!function(){alert('我是匿名函数')}() // 求反,我们不在意值是多少,只想通过语法检查。+function(){alert('我是匿名函数')}()-function(){alert('我是匿名函数')}()~function(){alert('我是匿名函数')}()void function(){alert('我是匿名函数')}()new function(){alert('我是匿名函数')}()
注意事项
虽说javaScript
的分号是可有可无的,但有些时候还是必要的。
console.log(5)(function add(a, b){var c = 2;console.log(a + b);})(1,2)
上面代码会报错,因为javaScript
确实是有分号这个规则的,代表着语句的结束。
这种时候加个分号就行了
console.log(5);(function add(a, b){var c = 2;console.log(a + b);})(1,2)
但是很多时候我们已经习惯不加分号了,显得比较麻烦,这种时候就要知道,什么情况下该加分号。
只要语句开头是括号,方括号,正则开头的斜杠,加号,减号的情况下,在前面加分号就行了,当然也可以加别的符号,比如感叹号,不过一般都是加分号
console.log(5);(function add(a, b){var c = 2;console.log(a + b);})(1,2)!(function add(a, b){console.log(a * b);})(3,2)
在对象里使用自执行函数
var a = 5var obj = {a: 1,b: (function(){console.log(this.a)// 5})(),c: 4}
var a = 5var obj = {a: 1,b: +function(){console.log(this.a)// 5}(),c: 4}
以上两种都可以,但需要注意的是对象里的立即执行表达式改变了原本this
的指向,这时的this
是window
var a = 5var obj = {a: 1,b: function(){console.log(this.a)// 1},c: 4}obj.b()
这种时候,this
是指向obj
参考下面博客:
/mafeifan/p/5881408.html
/tourey-fatty/p/12114649.html