在for循环中使用let 声明变量,出了for循环,为什么还能访问到这个变量

都说是let i 会创建一个块级作用域,那么for语句执行完以后,这个块级作用域应该就消失了呀,为什么我现在执行函数,依然能访问到这个块级作用里的变量,这个现象就和闭包是一样的。
希望能得到您帮助,请从从内存角度,结合执行期上下文,AO,GO,作用域,以及预编译来解释一下。

        let fnArr = Array(3);
        for(let i =0;i<3;i++){
            fnArr[i] = function(){
                console.log(i);
            }
        }

        fnArr[0]();
        fnArr[1]();
        fnArr[2]();

输出结果:
0
1
2

这个就是闭包。

要了解闭包的特性,就要先了解函数与局部作用域。

一个函数在执行时会先创建一个局部作用域,之后在这个函数内声明的变量与子函数都会放到这个局部作用域中。
闭包是指子函数会与其所在的局部作用域绑定在一起。只要这个子函数存在,子函数所在的局部作用域和局部作用域中的局部变量就一直存在,不会被系统回收。可以让子函数内始终能访问局部作用域中的局部变量。

而用 let 声明的是块作用域变量,这个块作用域与函数在执行时创建的局部作用域有一样的作用。
块作用域也可以与块作用域中的子函数形成“闭包”

当let在for中使用时每次循环都会在循环体的块作用域内创建一个新的i变量,
每次循环体块作用域都会与块作用域中的子函数形成“闭包”,因为闭包才让每次块作用域内的i变量保留,不会被系统回收。

你这里的 let i 创建一个块级作用域,是说在 for这个块作用域中创建了一个局部变量,也就是说 i 变量是 for 这个块作用域(执行上下文环境)的 局部变量,只能在 for这个块作用域中访问得到 ,在 for 外部是访问不到的 ,你可以在 for 外部 输出 i测试一下,是会报错的。

但是如果你在for循环中用的是 var i ,那么 i 就是定义在全局变量的 i,也就是说你在for 外部 是可以找到的,因为 在window中定义i

for(var i =0;i<3;i++)   {...} 
console.log(i)  // 输出3 

你的代码相当于以下代码

let fnArr = Array(3);

  fnArr[0] = function(){
    console.log(0);
  }
  fnArr[1] = function(){
    console.log(1);
  }
  fnArr[2] = function(){
    console.log(2);
  }

  fnArr[0]();
  fnArr[1]();
  fnArr[2]();
  console.log(i);  // 报错

我其实没太看懂你说的块级作用域该消失是什么意思,不知道是不是我上面说的意思
还是说 你觉得 for 循环结束后,fnArr0; 这条语句访问不到

fnArr是在for外部定义,在for循环内部改变了fnArr的值

你把fnArr中的每个元素改成了函数,循环结束了,数组还在,函数还在