定义
立即调用的匿名函数又被称作立即调用的函数表达式(IIFE, Immediately Invoked Function Expression)。
它类似于函数声明,但由于被包含在括号中,所以会被解释为函数表达式。
1 | // IIFE |
如果以function开头,则会被识别为函数声明,函数声明是不能被被执行符号()执行的!
1 | function (){ |
只要是函数表达式,就可以加执行符号达到立即执行的效果了。
也可以加上一元操作符将函数转成表达式 (不推荐使用)。
1 | var bar = function(){ |
很多人习惯在前面加;,就是为了避免两个立即执行函数连在一起时发生如下错误:以下代码只能输出一个a。
1 | (function(){ |
再补充一个奇怪的情况:
1 | function foo(a) { |
这里不会报错,也不会输出,实际上这种写法会被解释成一个函数声明,还有一个无意义的表达式。也就是下面的样子:
1 | // 函数声明 |
关于非匿名自执行函数
注意:立即执行函数也是可以加名字的,但是要注意,函数名只读。看下面这个例子
1 | var b = 10; |
在非严格模式下会输出[Function b],在严格模式下会报错!Uncaught TypeError: Assignment to constant variable.
原因就在于匿名函数属于表达式的范畴,如果添加了名字,遵从具名函数表达式的规范。
函数表达式中函数的识别名是不需要的,有名称的函数表达式,就是具名函数表达式 (Named function expressions, NFE),其函数的识别名,它的作用域是只在函数的主体内部。
1 | var b = 0; |
应用
进行初始化
在ES6的let和const,可以用立即执行函数来模拟块级作用域,避免全局变量污染。
1 | (function() { |
类似的,有一些操作需要在页面加载完成立即执行,比如绑定事件、创建对象等,也需要一些临时的变量,但是之后不会再用到。这时候使用立即执行函数,将这些初始化代码包裹在其局部作用域中,就是个很好的方案。
下面这个例子在初始化时绑定监听事件,count变量不会被泄漏出去,而且点击事件也能正常运作。
1 | (function() { |
模块化封装
使用一个立即执行函数创建的闭包,实现对象字面量创建对象的私有成员。以此来封装模块,暴露的接口成为公有方法以供调用,私有成员外部无法取得。
1 | var myobj = (function () { |
其他
经典题
下面这道经典题目
1 | for(var i = 0; i < 5; i++ ) { |
网上很多解释是说是因为事件队列的原因,说因为回调函数被放到事件队列中,for循环执行完毕i的值已经变成了5,再执行5个console.log(i)所以会输出5个5。
开始我看到这种解释有些困惑,为什么换成let就能解决呢?如果说是因为事件循环的原因,那换成let,循环完毕i也变成5了。
之后我补充了作用域,作用域链,执行上下文等知识,才了解到这种现象是由于作用域造成的。
之所以使用let就可以得到期望结果,是由于let的块级作用域,每一轮循环都会有一个新的词法作作用域环境保存每一轮的i。
1 | blockLexicalEnvironment = { |
之后执行console.log(i),由于当前作用域没有i,所以沿着作用域链找,即找到了他上一层的块级词法环境中的i。
所以使用这里使用立即执行函数也是同样的原理。
1 | for(var i = 0; i < 5; i++ ) { |
在迭代内使用 IIFE 会为每个迭代都生成一个新的作用域,使得延迟函数的回调可以将新的
作用域封闭在每个迭代内部,每个迭代中都会含有一个具有正确值的变量供我们访问。
立即执行函数的递归
立即执行函数是如果不加名字,又想要自身递归调用怎么办呢?可以使用 arguments.callee,已在ES5严格模式中被废弃,了解原因可以看这里。
callee是arguments对象的一个属性。它可以用于引用该函数的函数体内当前正在执行的函数。
1 | const init = (function(n){ |
当然,可以使用具名函数直接调用,如下:
1 | const init = (function a(n){ |
关于变量提升
匿名函数属于函数表达式,创建执行上下文时不会被提升。
1 | console.log(foo); // 正常输出 |
参考
https://blog.bitsrc.io/understanding-scope-and-scope-chain-in-javascript-f6637978cf53
Kyle Simpson. 2014. You Don’t Know JS: Scope & Closures (1st. ed.). O’Reilly Media, Inc.