学习ES6时let用于for循环时,函数里console的问题

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

既然循环结束后,数组a的每一项都是function(){console.log(i)},那么a[6]()输出是6是怎么实现的?难道let保存了10个状态?

之前搞错了,在外面不能输出i,以下是原来的问题

===============================

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

a[6] = function(){console.log(i)}
而此时i=10,那么a[6]为啥不是10

阅读 9.6k
8 个回答
> console.log(i);  //10
ReferenceError: i is not defined

let是块作用域声明,出了for循环就失效了~


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

在匿名函数作用域中log(i)引用了上层作用域的变量 i,构成闭包
a[i]中保存了10个闭包,各自保留了构成闭包时变量 i的值。


2个闭包栗子~

> var f=function(){
...  let i=0;
...  return function(){
.....  i=i+1
.....  return i
.....  }}()
undefined
> f()
1
> f()
2
> f()
3
> f()
4
> f()
5
> var fn;
undefined
> for(let i=-1;i<0;i++){
...     fn=function(){
.....         i=i+1
.....         return i
.....     }
... }
[Function]
> fn()
0
> fn()
1
> fn()
2
> fn()
3
>

看了@同意并接受 的答案,现在我终于搞明白了,总结一下:

这个例子是很多ES6教程里面都有的,是和用var的for循环作对比,

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

我对这两个例子的理解是:

用let是ES6语法,for循环代码块构成一个作用域,里面的function形成闭包,function里面可以引用父作用域的变量,引用的是function被定义时的父作用域变量值,所以在每一次执行a[i] = function () { console.log(i);}时,function都保存了当时的i值。

而for使用var时,还是ES5的写法,for代码块没有形成作用域,所以里面的function不构成闭包,所以在每一次执行a[i] = function () { console.log(i);}时,并不会保存当时i的值,所以只有当a[i]()这个函数执行时,才会使i值变现,这时,function沿着作用域链查找i的值,父作用域就是window,i的值经过十次i++,已经是10,所以a[6]()会输出10

重要背景:一个知乎上关于闭包的问题(如何通俗易懂的解释javascript里面的‘闭包’?),在这个问题里,看到这样一句话之后,我如同被闪电劈中(这什么破比喻==),对于闭包概念的模糊一下子都消散了,就是这样一句话:
JavaScript中的函数运行在它们被定义的作用域里,而不是它们被执行的作用域里。所以虽然我说了那么多,关键还是被被定义的作用域电光火石闪电劈中的一刹那

希望大家都能理解闭包( •̀∀•́ )

console.log(i) 应该是undefined才对吧。。。

你自己也说了a[6] = function(){console.log(i)},如果a[6]=10,那岂不是function(){console.log(i)}就是10? a[6]的值其实是这个匿名函数function(){console.log(i)},匿名函数也可以叫做函数表达式,换言之,这个表达式是a[6]的值。虽然表达式内部指示输出10,但是这不是a[6]。

let是块作用域声明
经过for 循环后的a  有 10 个 闭包 成员 
a[6](); //6  ;  i = 6
console.log(a[6]); //  a[i] = function(){console.log(i)}  ;  i = 6 
console.log(i);   //   i is not defined   外包是无法访问到 let 内部成员的

如果你把 let 换成 var 的话,你会神奇的发现 a[6]() 输出 10。
这里我感觉还是 i 的作用域决定的。
因为 i 的作用域仅仅是 for 循环内, 所以在函数中调用 i 的时候,需要形成一个闭包好让外部访问。
如果 换成 var,i 的作用域是这个 js 文件的全局,在函数中调用 i 的时候是可以找到这个变量的,无需形成闭包。
还有一个概念就是 var 的变量提升问题,你可以参考下这个。

a[6]() 这个后面跟的括号是什么意思?

let就是在{}中定义作用域,所以可以这样实现{}内定义作用域:

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

循环时,匿名函数中的i会自动找到全局作用域的i,形成闭包,i的值被保存下来。

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