搜尋

首頁  >  問答  >  主體

javascript - 这个函数是闭包吗?

var a = [];
for (var i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6](); // 10

在阮老师的es6网站上看到这段代码,书中描述的是
“上面代码中,变量i是var声明的,在全局范围内都有效。所以每一次循环,新的i值都会覆盖旧值,导致最后输出的是最后一轮的i的值。”
我觉得并不是这样,这个结果是闭包导致的。。。对吗?
原地址是:http://es6.ruanyifeng.com/#docs/let

天蓬老师天蓬老师2831 天前685

全部回覆(7)我來回復

  • 迷茫

    迷茫2017-04-11 11:45:29

    是闭包,而且有10个闭包。之所以出现你没有期望的结果,是因为10个闭包共享了相同的外部环境(即10对1),而i正好位于这个外部环境。

    解决办法通常有两种:

    var a = [];
    for (let i = 0; i < 10; i++) {
      a[i] = function () {
        console.log(i);
      };
    }
    a[6](); // 6

    var a = [];
    for (let i = 0; i < 10; i++) {
      a[i] = (function(j) {
        return function () {
          console.log(j);
        }
      })(i);
    }
    a[6](); // 6

    这两种方法的本质是一样的,都是给这十个闭包分别赋予一个独立的外部环境(即10对10)。

    回覆
    0
  • 迷茫

    迷茫2017-04-11 11:45:29

    是闭包啊,for循环中定义的函数持有函数外部的变量i。

    回覆
    0
  • 巴扎黑

    巴扎黑2017-04-11 11:45:29

    他这里是为了个let对比所以才这么说的,没有什么问题。
    而且,Javascript中函数都是闭包,所以这个结果也和闭包有关系。

    lmao,这个答案还有人-1

    回覆
    0
  • PHP中文网

    PHP中文网2017-04-11 11:45:29

    有人用了let实现,但是其中一个用var也是一样的,先上代码;

    var a = [];
    for (var i = 0; i < 10; i++) {
        a[i] = (function(i) {
            return function() {
                console.log(i);
            }
        })(i);
    }
    a[6]();

    或者

    var a = [];
    for (var i = 0; i < 10; i++) {
        function fun(i){
            return function(){
                console.log(i);
            }
        }
        a[i] = fun(i);
    }
    a[7]();

    你会发现这两种方法都有一个特点就是函数都执行了一遍,在你的方法中只是对函数进行了声明上下文不会发生改变,函数体中有一个变量,该变量来自外部,因此会保存在作用域链中,所以说其实这个变量实质上是外部i的引用,当for语句执行完毕之后,i为10,所以每一个函数都会输出10。
    而当我在每次声明的时候都让他执行一遍,在执行的时候上下文会发生改变,即传递进来的参数会形成新的变量,因为每一次执行都会形成一个新的上下文,所以数组中每一个元素都拥有自己独立的变量。

    回覆
    0
  • PHP中文网

    PHP中文网2017-04-11 11:45:29

    关于闭包你就记住一个函数在调用的时候可以取到所有它定义的时候作用域内的变量,就可以了。但是一定要理解是变量,以及是函数“定义时候”的作用域范围。

    回覆
    0
  • 怪我咯

    怪我咯2017-04-11 11:45:29

    ES5只有函数级作用域,由于函数及变量定义提升的缘故,你的代码实质上等于

    var a;
    var i;
    function unnamed() {
        console.log(i);
    };
    
    a = [];
    for (i = 0; i < 10; i++) {
      a[i] = unnamed;
    }
    a[6](); // 10

    所以你的a数组里指向的是同一个函数,调用时使用的是同一个闭包里的i。

    解决方法就是在循环里建立新的作用域,在新作用域里建立你的函数,ES6可以使用let,ES5则需要再建立一个函数如下

    // es6
    var a;
    a = [];
    for (let i = 0; i < 10; i++) {
      function unnamed() {
        console.log(i);
      }
      a[i] = unnamed;
    }
    a[6]();
    // es5
    var a;
    var i;
    function unnamed(i) {
      function unnamed2() {
        console.log(i);
      }
      return unnamed2;
    }
    
    a = [];
    for (i = 0; i < 10; i++) {
      a[i] = unnamed(i);
    }
    a[6]();

    回覆
    0
  • 怪我咯

    怪我咯2017-04-11 11:45:29

    这里是在全局执行的,是没有闭包的。出现这种情况是由于var i是在全局有效的而作为对比的let只是在for循环的局部作用域有效

    回覆
    0
  • 取消回覆