关于ES6的 let 命令

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

我知道let定义的变量具有块级作用域,但还是不明白为什么输出的是6,求解释原理。
另外这里也有闭包形成吧?

阅读 5.7k
6 个回答

let定义的变量i具有块级作用域,作用域是for循环所包含的语句块。
这个for循环最终执行了10次,实际上会产生10个互相平行的块级作用域。在每个作用域里面,i的值分别是0,1,2,..,8,9。
当调用a[6]时,执行到console.log(i)这行语句时,解释器会沿着作用域链寻找i的值,结果向上一层就是这10个平行作用域中的第7个,其中i的值为6,于是就打印出了6。
这段代码如果被翻译成ES5代码,应该如下:

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

实际上,使用babel 6.6.5 babel-preset-es2015 6.6.0 的转换结果如下:

"use strict";

var a = [];

var _loop = function _loop(i) {
  a[i] = function () {
    console.log(i);
  };
};

for (var i = 0; i < 10; i++) {
  _loop(i);
}

i是for循环这个作用域的局部变量,每次进入for循环都会重新创建,所以i对应的值为1、2...6。我理解,这里应该是有形成闭包的,闭包其实就是函数持有其外层作用域的引用,能够访问外层作用域的变量。如果for循环里面没有那个打印函数,for语句执行完之后,这个块级作用域应该会销毁掉。由于循环中的函数,for执行完之后i并没有回收,这和通常的函数级作用域嵌套道理貌似是一样的。

块级作用域:{{let a="block scope"} {...a is undefined,you can redefine,but the first a can not be changed}}
代码,改let为var:

          var a = [];

for (var i = 0; i < 10; i++) {
a[i] = function () {

console.log(i);

};

}
console.log(i) =>10////i为全局变量,循环结束后值为10
console.log(a6 //=>10
console.log(a[6].toString())=> function () {console.log(i);}
a=>[function () {console.log(i);},function () {console.log(i);},...]//i为10
换成let,记住块级作用域的特性;
a同样为[function () {console.log(i);},function () {console.log(i);},...]//i递增,互相不影响(块级作用域,或者你可以理解为所谓的十个平行作用域)。
。。。。。。。。。。。。。。。。。。。。。。。。。。分割线
但是 以为这样这样理解就正确了吗?naive
用var a同样为[function () {console.log(i);},function () {console.log(i);},...]//我没有块级作用域,但是我有函数作用域分割啊,我同样的相互不影响。所以还原问题的本质:
看代码:var
图片描述

执行结果(后面截不到)
图片描述
每一次循环,i都会++,a中各元素的i都会被重新覆盖。

let
图片描述

执行结果
图片描述
每一次循环i都会++,但是a中各元素的i都没有被重新覆盖。而函数在声明时作用域链就已经确定,为什么不能被覆盖呢。要知道在某些方面块级作用域的表现和函数作用域的表现类似:
图片描述
执行结果
图片描述
在函数内声明的变量在函数体及其嵌套的函数中都是defined,但是其好像又破坏了(函数在声明时作用域链就已经确定)这个规则:for循环体中的i不能覆盖a中元素(函数)的i!ok按目前的理论:let的表现就是这样。。。
蹩脚(函数在声明时作用域链就已经确定)的解释就是:i为for循环体中的变量,只在循环体中有定义,而a为全局变量,i的手没那么长。

没看出什么闭包,也没觉得跟 let 有啥关系。就普通的一个循环,把表达式储存在数组中:

[function () {console.log(0)}, function () {console.log(1)}, function () {console.log(2)} ...]

所以 a[6] 应该是表达式 function () {console.log(6)} 本身。所以结果是:

(function () {console.log(6)})(),那自然就是 6 咯。

这不是闭包。
在你读取i的值时,循环早已结束,i已经是6了。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
1 篇内容引用
推荐问题
宣传栏