上述代码中当解析到console.log(i);时,会报错ReferenceError: i is not defined,这是因为它访问的变量是在IIFE内部定义的,在外部访问不到。
在es5以前,为了防止变量定义外泄,IIFE是个非常有效的方式,这样也不会导致闭包相关的内存问题,因为不存在对这个匿名函数的引用。因此,只要函数执行完毕,其作用域链就可以被销毁。
IIFE的全称为Immediately Invoked Function Expression,翻译过来就是立即调用函数表达式。
模拟块级作用域
使用IIFE可以模拟块级作用域,即在一个函数表达式内部声明变量,然后立即调用这个函数,这样位于函数体作用域的变量就像是在块级作用域中一样(如上述例子所示)。
在ES6以后,新增了块级作用域的概念,因此我们想实现同样的效果,就无需再使用IIFE了,我们用let来重写下上面的例子,代码如下所示:
- for (let i = 0; i < 5; i++) {
- console.log(i);
- }
- console.log(i);
有关变量作用域的更多知识点请移步我的另一篇文章:深入理解作用域和闭包
块级作用域无法替代立即调用函数的表达式,当你的代码在不支持ES6+的浏览器上运行时,你不得不求助立即执行函数来模拟。
实现私有变量
IIFE可以返回一个函数引用,当这个函数在IIFE的词法范围外执行,也会创建一个闭包,使函数能够访问局部变量。
我们举个例子来说明下,如下所示:
- const getOrderId = (function() {
- let count = 0;
- return function() {
- ++count;
- return `id_${count}`;
- };
- })();
- console.log(getOrderId());
- console.log(getOrderId());
- console.log(getOrderId());
- console.log(getOrderId());
上述代码中:
- 创建了一个自执行函数,其返回一个函数引用
- 自执行函数内部有一个变量count,它就是一个私有变量,外部无法访问
- 最后,返回一个函数引用,形成闭包结构,对count自增后与_id进行拼接并返回
在IIFE之外无法访问函数内部的count变量,除了从IIFE中返回的函数,别处无法读写该变量,这样就能创建真正的私有状态变量。
变量重命名
在平常开发中可能遇到两个不同的库,他们暴露的全局变量名却是相同的,例如:正在使用Jquery,另一个库也指定了一个名为$的全局变量。
为了解决命名冲突问题,可以将一段代码封装在一个IIFE中,将一个全局变量(比如Jquery)作为参数传入IIFE,在函数内部,就可以以一个任意的参数名(比如 $)来访问该参数值,我们举个例子来说明下,如下所示: