let 和 var 的区别以及什么是暂时性死区?let配合for循环和var配合for循环有什么区别?

目录

1、var有变量提升,let 没有变量提升

2、var 可以重复声明,而let不能重复声明

3、let可以定义块级作用域 { }

4、临时失效区 (暂时性死区)

① 什么是暂时性死区?

② 表现在哪里?

5、let 配合for循环的独特应用

① 用 var 配合 for 循环,有导致什么问题?

② let非常适合用于 for循环内部的块级作用域

6、拓展

① 为什么ES6规定暂时性死区和 let 、const 语句不出现变量提升?

② 为什么ES6中需要块级作用域?


1、var 有变量提升,let 没有变量提升

变量提升 :将变量提升到当前作用域的上边

      console.log(a) // 定义了没有赋值,值为 undefined
      var a = 10
      console.log(b) // 报错 Uncaught ReferenceError: b is not defined。
      let b = 10

      var a = 99
      f()
      console.log(a)
      function f() {
        console.log(a)
        var a = 10
        console.log(a)
      }

输出结果:undefined           10             99        

(注意:这里是先触发 f 函数:变量 a 在函数内进行了提升,所以打印的时候是 undefined)

2、var 可以重复声明,而let不能重复声明

var可以重复声明 

var c = 10
var c = 20
console.log(c) // 20

let不允许在相同作用域内,重复声明同一个变量。

否则报错:Uncaught SyntaxError: Identifier 'XXX' has already been declared

let c = 10
let c = 20
console.log(c) // 报错
// 报错
function func() {
  let a = 10;
  var a = 1;
}

// 报错
function func() {
  let a = 10;
  let a = 1;
}
function func(arg) {
  let arg;
}
func() // 报错


function func(arg) {
  {
    let arg;
  }
}
func() // 不报错

3、let可以定义块级作用域 { }

var 是全局声明;

      var m = 5
      if (m) {
        var n = 100
      }
      console.log(n) // 100

let 可以定义块级作用域变量,是局部变量,只在它当前的代码块内有效,而且有暂时性死区的约束。 

      var m = 5
      if (m) {
        let n = 100
      }
      console.log(n) // 报错

下面的函数有两个代码块,都声明了变量 n,运行后输出 5。

这表示外层代码块不受内层代码块的影响

如果两次都使用var定义变量n,最后输出的值才是 10。  

function f1() {
  let n = 5;
  if (true) {
    let n = 10;
  }
  console.log(n); // 5
}

4、临时失效区 (暂时性死区)

① 什么是暂时性死区?

ES6 明确规定,如果区块中存在 let 和 const 命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错

使用 let 或 const 声明的变量,会针对这个变量形成一个封闭的块级作用域,在这个块级作用域当中,如果在声明变量前访问该变量,就会报 referenceError 错误;如果在声明变量后访问,则可以正常获取变量值: ​​​​​​​ 

总之,在代码块内,使用 let 命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区” (当前作用域不允许同名的变量进来)

function foo() { 
  let bar = 3 
  console.log(bar) 
} 
foo()

这段代码正常输出 3。因此在相应花括号形成的作用域中,存在一个“死区”,起始于函数开头,终止于相关变量声明的一行。在这个范围内无法访问 let 或 const 声明的变量。

② 表现在哪里?

用 let 声明的变量,不存在变量提升。而且要求必须 等 let 声明语句执行完之后,变量才能使用,不然会报Uncaught ReferenceError错误。

暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。 

      var x = 10
      function a() {
        console.log(x)
        let x = 20
        // console.log(x);
      }
      a()

      var x = 10
      function a() {
        // console.log(x)
        let x = 20
        console.log(x); // 20
      }
      a()
if (true) {
  // TDZ开始
  tmp = 'abc'; // ReferenceError
  console.log(tmp); // ReferenceError

  let tmp; // TDZ结束
  console.log(tmp); // undefined

  tmp = 123;
  console.log(tmp); // 123
}

5、let 配合for循环的独特应用

① 用 var 配合 for 循环,有导致什么问题?

var配合for循环,此时输出了10个10。

下面代码中,变量ivar命令声明的,在全局范围内都有效,所以全局只有一个变量i

每一次循环,变量i的值都会发生改变,而循环内被赋给数组a的函数内部的console.log(i),里面的i指向的就是全局的i

也就是说,所有数组a的成员里面的i,指向的都是同一个i,导致运行时输出的是最后一轮的i的值,也就是 10

        for (var i = 0; i < 10; i++) {
            setTimeout(function () {  // 同步注册回调函数到 异步的 宏任务队列。
                console.log(i);        // 执行此代码时,同步代码for循环已经执行完成
            }, 0);
        }

 同步注册回调函数到异步的宏任务队列,当执行到打印的时候,同步代码for循环已经执行完成。

② let非常适合用于 for循环内部的块级作用域

JS中的for循环体比较特殊,次执行都是一个全新的独立的块作用域。

用 let 声明的变量传入到 for 循环体的作用域后,不会发生改变,不受外界的影响。

        for (let i = 0; i < 10; i++) {
            setTimeout(function () {
                console.log(i);    //  i 是循环体内局部作用域,不受外界影响。
            }, 0);
        }

let配合for循环,使用输出0 1 2 3 4 5 6 7 8 9

i 虽然在全局作用域声明,但是在for循环体局部作用域中使用的时候,变量会被固定,不受外界干扰。 

6、拓展

① 为什么ES6规定暂时性死区和 let 、const 语句不出现变量提升?

主要是为了减少运行时错误,防止在变量声明前就使用这个变量,从而导致意料之外的行为。

② 为什么ES6中需要块级作用域?

ES5只有全局作用域和函数作用域,没有块级作用域,这带来了很多不合理的场景。

01、内层变量可能会被覆盖外层变量

var tmp = new Date();

function f() {
  console.log(tmp);
  if (false) {
    var tmp = 'hello world';
  }
}

f(); // undefined

上面代码的原意是,if代码块的外部使用外层的tmp变量,内部使用内层的tmp变量。

但是,函数f执行后,输出结果为undefined,原因在于变量提升,导致内层的tmp变量覆盖了外层的tmp变量。 

02、用来记数的循环变量泄露为全局变量

下面代码中,变量i只用来控制循环,但是循环结束后,它并没有消失,泄露成了全局变量。

var s = 'hello';

for (var i = 0; i < s.length; i++) {
  console.log(s[i]);
}

console.log(i); // 5

块级作用域的出现,实际上使得获得广泛应用的匿名立即执行函数表达式(匿名 IIFE)不再必要了。

// IIFE 写法
(function () {
  var tmp = ...;
  ...
}());

// 块级作用域写法
{
  let tmp = ...;
  ...
}

ES6 入门教程

7、总结

① let 和 var 的区别

var 存在变量声明提升;let 不存在变量声明提升。

在相同作用域内,var 可以重复声明;let 不允许重复声明同一个变量。

var 是全局声明;let 可以定义块级作用域,是局部变量,只在当前代码块内有效,而且有暂时性死区的约束。

 ② 暂时性死区

如果在某个作用域中存在 let 和 const 命令,这个作用域从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。

总之,在代码块内,使用 let 命令声明变量之前,该变量都是不可用的。

这在语法上,称为“暂时性死区” (当前作用域不允许同名的变量进来)

暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。 

③ let 配合 for 循环 和 var 配合 for 循环有什么区别?

var 配合 for 循环,var 会声明一个全局变量 i,每次循环都是在改变同一个变量 i,会导致异步输出的结果都是同一个值;还会导致循环变量泄露为全局变量。

而 let 配合 for 循环,每次执行都是全新的独立块级作用域,不会被新循环时覆盖,let非常适合用于 for循环声明计数器 i ;

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值