JS中的IIFE

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中定义的任何变量和函数,都会在执行结束时被销毁,这种做法可以减少闭包占用的内存问题,因为没有指向匿名函数的引用。只要函数执行完毕,就可以立即销毁其作用域链了。

欢迎关注微信公众号

猜你喜欢

转载自juejin.im/post/5c2f4c8a518825255d2957d1
今日推荐