问答

JavaScript let 块级作用域对作用域链的影响?

作者:admin 2021-04-10 我要评论

题目描述: for (var i = 0; i 3; i++) { setTimeout(function () { console.log(i); //依次输出3,3,3 }, 1000); }for (let j = 0; j 3; j++) { setTimeout(func...

在说正事之前,我要推荐一个福利:你还在原价购买阿里云、腾讯云、华为云服务器吗?那太亏啦!来这里,新购、升级、续费都打折,能够为您省60%的钱呢!2核4G企业级云服务器低至69元/年,点击进去看看吧>>>)

题目描述:

for (var i = 0; i < 3; i++) {  
      setTimeout(function () {
        console.log(i);  //依次输出3,3,3
      }, 1000);
    }

for (let j = 0; j < 3; j++) {
  setTimeout(function () {
    console.log(j);  //依次输出0,1,2
  }, 1000);

为什么用let会输出0、1、2?

  • 百度以及个人思考,
  • 得出的结果是
  • 函数在声明时会同时保存当时的作用域链,就是说当时的活动变量
  • 在setTimeout中使用变量时会沿着作用域链去寻找该变量,

对于用上面let 声明的循环来说,由于块级作用域,每次循环相当于--

{
  let j = 0;
  function () {
    console.log(j); 
  }
}
   //第二次循环
{
  let j = 1;
  function () {
    console.log(j); 
  }
}
   //第三次循环
{
  let j = 2;
  function () {
    console.log(j); 
  }

这些块级作用域相互独立,互不影响
综上,用let,输出的才是0、1、2
问题

如果说是因为声明函数时作用域链就记录了当时的i值,那用var声明的就不会被记录吗?

还是我得到的答案错了,是因为别的原因导致输出不同?

对于所给两段for循环过程中,JavaScript围绕let变量、块级作用域、作用域链这几个关键词到底做了什么?

###
  • 每次执行到循环体中的setTimeout方法,该方法都会将调用的回调函数放入“任务队列中”,等待主线程(执行栈中)的事件全部执行完毕后,执行队列头的事件。
  • 第一次循环后,任务队列中只有一个被setTimeout方法放入的回调函数,其作用域链中记录的是i的初始值0
  • 由于let 声明的变量只存在于块级作用域内,因此每一次循环体执行完毕销毁该变量,然后在for循环出的新块let声明一个新的变量j,按for循环原本既定的顺序为其赋值,然后执行循环体
  • 因此第二次循环时,任务队列中的回调函数的作用域链中,记录的是新创建的,重新被赋值为1的变量i
  • 正是由于块级作用域相互独立互不影响,才不会覆盖j的值,就此,我有点理解为什么let能防止数据污染了(还有es6规定let不能重复声明这一点)
###

你对 let 的理解没有错,造成 var 的诡异行为的原因是早期 JS 没有块级作用域,那时候所用的关键字 var 声明的变量是在就近的函数作用域里。
所以

;(function(){
    'use strict';
    // var 在这里有效
    if (true) {
        var someUniqueVariation = 123;
    }
    console.log('someUniqueVariation:', someUniqueVariation);
})();

可以执行;

;(function(){
    'use strict';
    // let 在这里无效
    if (true) {
        // let 只在这里有效
        let someUniqueVariation = 123;
    }
    console.log('someUniqueVariation:', someUniqueVariation);
})();

会报错。

###

var声明的当然也被记录了,但是var由于是函数级作用域,在for循环里并不会重复产生新变量,而是原来的变量++,最后变成了3

###

看一看一看这几篇
csdn
StackOverflow

var funcs = [];
for (var i = 0; i < 3; i++) {
  funcs[i] = function() {
    console.log(i);
  };
}
for (var j = 0; j < 3; j++) {
  funcs[j]();
}//333
let a = [];
for (let j = 0; j < 3; j++){
  a[j] = function() {
    console.log("He value: "+ j)
  }
}
for (let x = 0; x < 3; x++){
  a[x]()
}///012
###

其实背后的原理就是闭包,
闭包使得内部 setTimeout 对外部依赖的 j 值不被释放,
从而达到输出 0、1、2 的效果

版权声明:本文转载自网络,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。本站转载出于传播更多优秀技术知识之目的,如有侵权请联系QQ/微信:153890879删除

相关文章
  • nginx响应速度很慢

    nginx响应速度很慢

  • 点击选中的多选框,会在已选那一栏显示

    点击选中的多选框,会在已选那一栏显示

  • PHP 多态的理解

    PHP 多态的理解

  • 关于C语言中static的问题

    关于C语言中static的问题

腾讯云代理商
海外云服务器