JavaScript基础—立即执行函数(IIFE)

一、立即执行函数(IIFE)

JavaScript基础—函数中介绍了函数作用域的概念:在函数中声明的变量在整个函数体内都是可见的,在函数的外部是不可见的。

不在任何函数内部声明的变量是全局变量,在JavaScript程序中都是可见的。ES6新增了块级作用域。

那么,在ES6之前我们能否弥补整个作用域的缺陷呢。那就是立即执行函数。

IIFE:全拼Imdiately Invoked Function Expression,是一个在定义的时候就立即执行的JavaScript函数

像如下的代码所示,就是一个匿名立即执行函数:

(function(window, undefined){
  // 代码...  
})(window);

注意:上述代码中最外层有两个圆括号,function关键字前面对应的为第一个括号,最后(window)的为第二个括号

二、IIFE中括号的含义

  2.1 第二个括号的意义

在JavaScript中,圆括号()是一种运算符,它跟在函数名之后,表示函数的调用,比如console.log(),调用的是console对象的log方法。因此就有了最后的这个括号,加粗标黄的括号:

函数部分();

因此第二个括号表示函数的调用,同样,这个括号中的参数便是函数调用时传入的实参。

  2.2 第一个括号的意义

在JavaScript中,什么才能执行,当然是函数或者函数表达式啦。但是我们能不能直接用function打头,定义一个函数呢,像下面这样:

function (){
  console.log(1)
}();

显然不行了,直接运行报错:

为什么呢?这跟浏览器的JavaScript引擎解析和function这两个东东有关。

function,这个关键字既可以当作语句,也可以当作表达式。当你以function打头的时候,浏览器蒙圈了,不知道该怎么解析了。这里就存在了歧义。

为了避免解析上的歧义,JavaScript引擎规定:如果关键字function出现在行首,一律解析为函数声明语句。因此,JavaScript引擎看到行首是function关键字之后,认为这一段都是函数的定义,不应该以圆括号结尾,所以就报错了。

那么为了这个东东能够执行,所以要让它转换成一个表达式,所以最简单的处理就是不要让function出现在行首,让JavaScript引擎把它看作是一个表达式,所以自然的就用括号把它报过了起来了。

因此,第一个括号的意义:把function(){}转化成一个可执行的函数表达式

  2.3 第一个括号不是必须是括号

像一些库的源码,喜欢用如下的方式代替:

~function(){
  // 代码...
}();

或者这种方式:

+function(){
  // 代码...
}();

但是其作用都是一样:把function(){}转化成一个可执行的表达式,方便执行。尽管有各种写法,但是我们还是推荐用圆括号包裹的方式,因为这样代码看起来更清晰,更明了,更有层次整体感

三、IIFE的目的

 在第一部分中提到IIFE实际是为了解决作用域缺陷而诞生的,那么它的诞生给我们带来了哪些好处呢?

  • 不必为函数命名,避免了污染全局变量;

  • IIFE内部形成了单独的作用域,可以封住一些外部无法读取的私有变量

以下是一个常规的写法:

function myModule(){
  //模块代码
  //这个模块所使用的所有变量都是局部变量
  //而不是污染全局命名空间
}
myModule(); // 但是我们不能忘记还要调用它

这种常规的写法依然定义了一个全局的变量myModule,麻烦并且污染来的全局变量。

以下是IIFE的写法:

// 匿名函数立即执行
(function(){
  //模块代码
  //这个模块所使用的所有变量都是局部变量
  //而不是污染全局命名空间
})(); // 函数结束定义并立即调用它

四、IIFE的参数

 在前面提了一下IIFE的参数传递,直接上代码:

var mymodule= {};
(function(window, MyModule, undefined){
    //代码
})(window, mymodule);

参数分为形参和实参。function(window, MyModule, undefined)三个参数为形参,第二个括号(window, mymodule)的两个参数为实参。也即可以理解为 window == windowMyModule== mymodule

  4.1 普通形参

普通形参是指由windowwall这样的实际变量传入指定,可以为任何类型的变量。一个形参就对应一个实参

  4.2 特殊形参undefined

大家都知道,IE是个神奇的浏览器,尤其是早期版本。比如说IE6这玩意儿,它居然功能强大到可以修改undefined,如果undefined被修改以,那么下面这类代码就玩不转了:

if(mymodule == undefined){
 //永远也进不来了
}

所以这个地方多加一个形参,就可以避免这个坑,在IIFE作用域中就能正常的获取到undefined了。

同时,它也有助于代码的压缩,减小文件的大小。

五、写法解析

最后给大家一个用IIFE写模块的一个代码模板:

// 定义
(function(window, MyModule, undefined){
    MyModule.name = 'mymodule'
    // 给wall命名空间绑定方法say
    MyModule.say = function(){
        console.log('hello');
    }
    MyModule.MyName = function(){
        console.log(this.name);
    }
})(window, window.mymodule  || (window.mymodule = {}));

// 调用
console.log(mymodule.name);
mymodule.say();
mymodule.MyName();

关键点在于传参部分:window.mymodule  || (window.mymodule = {}),这里巧妙的运用了或运算的短路原则

  • 如果window.mymodule 是已经实例化的,非not defined。则直接返回window.mymodule的引用,赋值给形MyModule。不会执行||运算符后面的内容。

  • 如果window.mymodule还未实例化,则进行实例化。这里要注意的点是实例化是一个赋值操作,需要用括号包起来,变成表达式去执行,才不会报错。

  • 表达式(window.mymodule = {})执行完毕后,会返回新对象window.mymodule的引用。

以上是一些立即执行函数的IIFE的一些基础知识,希望能对你有所帮助。欢迎关注同步微信公众号:前端小菜的进阶之路

公众号:Mr-Small_Teem

猜你喜欢

转载自blog.csdn.net/jingsi1991/article/details/84262143
今日推荐