IIFE
IIFE: Immediately Invoked Function Expression, 翻译过来就是立即调用的函数表达式. 也就是说, 在函数声明的同时立即调用这个函数.
普通的函数声明和函数调用
- function bar() {
- var a = 10;
- console.log(a);
- }
- bar(); // 函数调用
IIFE 函数声明和调用
- (function foo(){
- var a = 10;
- console.log(a);
- })();
首先注意一点的就是 IIFE 函数是由一对 () 将函数声明包裹起来的表达式. 使得 JS 编译器不再认为这是一个函数声明, 而是一个 IIFE, 即立刻执行函数表达式. 但是两者达到的目的都是一样的, 都是声明了一个函数并且随后调用这个函数.
为什么要使用 IIFE
如果只是为了立即执行一个函数, 显然 IIFE 所带来的好处有限. 实际上, IIFE 的出现是为了弥补 JS 在 scope 方面的缺陷: JS 只有全局作用域(global scope), 函数作用域(function scope), 从 ES6 开始才有块级作用域(block scope). 对比现在流行的其他面向对象的语言可以看出, JS 在访问控制这方面是多么的脆弱! 那么如何实现作用域的隔离呢? 在 JS 中, 只有 function 才能实现作用域隔离, 因此如果要将一段代码中的变量, 函数等的定义隔离出来, 只能将这段代码封装到一个函数中.
为调用一次的函数达到作用域隔离, 命名冲突, 减少内存占用问题.
块级作用域
在 ES5 中是没有块级作用域的概念. 只有全局作用域和函数作用域
- for (var i = 0; i< 10; i++) {
- console.log(i);
- }
- console.log(i); // 10
这个地方可以看出 for 循环中的变量 i 是一个全局变量, 在 for 循环执行完毕后这个 i 还是可以访问到的. i 并没有被销毁
块级作用域也可以称为私有作用域. 也就是说只在 for 循环的语句块中有定义, 一旦循环结束, 变量 i 就会被销毁, 而在 ES5 中我们主要使用匿名函数[IIFE] 的方式来达到块级作用域的效果.
IIFE 实现作用域隔离
- // 函数声明语句写法
- function test() {
- };
- test();
- // 函数表达式写法
- var test = function(){
- };
- test();
[注意]JavaScript 引擎规定, 如果 function 关键字出现在行首, 一律解释成函数声明语句; 而函数声明后面是不能跟圆括号的(匿名函数是函数声明的一种). 然而, 函数表达式的后面可以跟圆括号. 所以可以将函数声明转换成函数表达式. 在立即执行函数中, 定义的变量会在立即
所以, 解决方法就是不要让 function 出现在行首, 让引擎将其理解成一个表达式 最常用的几种办法
- (function(){
- console.log('123');
- }());
- (function(){
- console.log('123');
- })();
- (function foo(){
- console.log('123');
- })();
分号
对于立即执行函数末尾的; 分号, 最好加上, 因为如果不加, 遇到两个都是用括号 () 包裹执行的 IIFE 时, 就会遇到问题.
- (function(){
- console.log('a');
- })()
- (function(){
- console.log('b');
- })()
这一段代码只有 a 可以显示出来, b 会报错
TypeError: (intermediate value)(...) is not a function
不加分号, 上面的内容会被 JS 理解为:
- (function(){
- console.log('a');
- })()(function(){
- console.log('b');
- })()
也就是说, a 匿名函数执行后, 没有; 的隔断, 后面的 b 立即执行函数与 a 合成了一个函数. 执行到输出完 a 时, 没有 reutrn, 后面的 () 相当于对 undefined 进行了执行, 所以报错.
我们可以这样修改
- (function(){
- console.log('a');
- })()
- (function(){
- console.log('b');
- }())
这样修改后, a,b 都会显示出来, 但是同样会报错, 还有其他的修改方式, 但是我建议加上; 分号避免报错.
IIFE 的优点
创建块级 (私有) 作用域, 避免了向全局作用域中添加变量和函数, 因此也避免了多人开发中全局变量和函数的命名冲突.
IIFE 中定义的任何变量和函数, 都会在执行结束时被销毁, 这种做法可以减少闭包占用的内存问题, 因为没有指向匿名函数的引用. 只要函数执行完毕, 就可以立即销毁其作用域链了.
来源: https://juejin.im/post/5c2f4c8a518825255d2957d1