JavaScript

JS中的IIFE和闭包

2019-10-08  本文已影响0人  西瓜鱼仔

IIFE( 立即调用函数表达式)

是一个在定义时就会立即执行的JavaScript函数。

(function () {
    statements
})();

这是一个被称为自执行匿名函数的设计模式,主要包含两部分。第一部分是包围在 圆括号运算符 () 里的一个匿名函数,这个匿名函数拥有独立的词法作用域。这不仅避免了外界访问此 IIFE 中的变量,而且又不会污染全局作用域。

第二部分再一次使用 () 创建了一个立即执行函数表达式,JavaScript 引擎到此将直接执行函数。

IIFE有以下特性:
1.当函数变成立即执行的函数表达式时,表达式中的变量不能从外部访问。

(function () { 
    var name = "Barry";
})();
// 无法从外部访问变量 name
name // 抛出错误:"Uncaught ReferenceError: aName is not defined"

2.将IIFE分配给一个变量,不是存储IIFE本身,而是存储IIFE执行后返回的结果。

var result = (function () { 
    var name = "Barry"; 
    return name; 
})(); 
// IIFE 执行后返回的结果:
result; // "Barry"

闭包

我个人理解是:因为作用域的存在,外部函数调用某个函数后,其变量对象本应该被销毁,但闭包的存在使我们仍然可以从外部访问此函数的变量对象(类比理解:比如windows系统删除正在使用的文件是删除不了的,因为文件正在被使用)。

在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“。在本质上,闭包是将函数内部和函数外部连接起来的桥梁。
演示代码:

function show() {
      var num = 666;
      return function () {
        console.log(num);
      }
    }

    var show2 = show();   //show函数在此执行一次
    show2();

打印结果是:666

IIFE和闭包的结合使用

1)ES5没有块作用域带来的影响

首先看一段代码:

 var arr = [];
    for (var i=0;i<3;i++){
      arr[i] = function () {
        return i;
      };
    }
    console.log(arr[0]());
    console.log(arr[1]());
    console.log(arr[2]());

按照我们心理预期,结果打印应该是:0,1,2
但是最终打印结果却是:3,3,3。这是为什么呢?
我们把上述代码稍作修改:

 var arr = [];
    for (var i=0;i<3;i++){
      arr[i] = function () {
        return i;
      };
    }
    console.log(arr[0]);   //修改的地方,去掉了()
    console.log(arr[1]);   //修改的地方,去掉了()
    console.log(arr[2]);   //修改的地方,去掉了()

打印结果为:


由此可见,由于函数未实际执行,for 循环并没有把i的具体值给arr[],而是让i待命。
由于ES5没有块作用域,for 循环条件中定义的变量 i 实际上是一个全局变量,这个 i 会随着 for 的执行,不断被覆写。直到它的值变为3,当页面加载完来执行 arr[0]() 函数时,arr[0]() 中待命的 i 会找 i 的最终结果,所以最后打印出来的是3,3,3。

2)使用IIFE+闭包解决ES5没有块作用域带来的影响。

直接上代码:

  var arr = [];
    for (var i=0;i<3;i++){
      //使用IIFE
      (function (i) {
        arr[i] = function () {
          return i;
        };
      })(i);
    }
    console.log(arr[0]());
    console.log(arr[1]());
    console.log(arr[2]());

因为IIFE是立即执行,所以 i 具体值会被写进 arr[0]() 中,最后打印结果为:0,1,2

3)附加:使用更简单的ES6语法来解决

var arr = [];
    for (let i=0;i<3;i++){
      //使用IIFE
      (function (i) {
        arr[i] = function () {
          return i;
        };
      })(i);
    }
    console.log(arr[0]());
    console.log(arr[1]());
    console.log(arr[2]());

最后打印结果为:0,1,2.
这是因为ES6语法增加了块作用域,for 循环的值不会再随着循环执行不断覆写,而是每次循环都把 i 的值单独保存下来。

上一篇下一篇

猜你喜欢

热点阅读