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 ;

<think>嗯,用户的问题是关于在JavaScript循环中使用let而不是var的安全性原因。我需要结合用户提供的引用资料来整理答案。首先,我应该回忆一下varlet的基本区别,比如作用域、变量提升重复声明这些方面。 根据引用[3][4],使用var循环中会导致变量i被提升到函数作用域,而let则是块级作用域。这意味着每次循环使用let时,都会创建一个新的绑定,每个闭包引用的是不同的i值。而var的情况下,i是唯一的,所有闭包都引用同一个变量,导致循环结束后i的值是最终的值,比如3,所以所有闭包返回的都是3。 然后,引用[2]提到var的变量提升可能引起问题,比如在循环体内影响其他部分。而let不存在变量提升,避免了这类问题。另外,引用[5]指出使用var时可以通过闭包解决,但默认情况下let更安全,不需要额外处理。 还需要考虑重复声明的问题,引用中提到var允许重复声明,而let不允许,这样在循环中使用let可以减少错误。例如,如果在循环内部不小心再次声明i,使用let会报错,而var不会,这样可能引入bug。 总结下来,主要原因应该是块级作用域、避免闭包陷阱、无变量提升禁止重复声明这几个点。需要将这些点用用户容易理解的语言解释清楚,并结合例子说明差异,比如用户给的createCounters函数例子,用var时输出都是3,而用let则输出0、1、2。 最后,生成相关问题的时候,要围绕这些区别展开,比如块级作用域的好处、变量提升的问题、闭包中的表现差异等。</think>### JavaScript循环letvar的安全性差异分析 在JavaScript循环中使用$let$相较于$var$更安全的核心原因在于**作用域机制****变量生命周期**的差异。以下将通过4个关键维度解析其区别: #### 一、作用域范围差异 1. **$var$的函数作用域** $var$声明的变量会提升到函数顶部,整个函数内共享同一个变量。在循环中所有闭包引用的都是同一个变量地址: ```javascript for (var i=0; i<3; i++) { setTimeout(() => console.log(i)) // 输出3次3 } ``` 循环结束后$i$的值为3,所有回调函数都引用最终值[^4]。 2. **$let$的块级作用域** 每次迭代都会创建新的词法环境,形成独立的变量副本: ```javascript for (let j=0; j<3; j++) { setTimeout(() => console.log(j)) // 输出0,1,2 } ``` 每个闭包捕获的是当前迭代的$j$值[^3]。 #### 二、变量提升问题 - **$var$存在变量提升** 声明会被提升到作用域顶部,但赋值保持原位: ```javascript console.log(x); // undefined(不报错) var x = 5; ``` 可能导致循环体内意外覆盖外部变量[^2]。 - **$let$的暂时性死区** 声明前访问会报错,强制开发者规范代码: ```javascript console.log(y); // ReferenceError let y = 10; ``` #### 三、重复声明风险 - **$var$允许重复声明** 可能导致意外覆盖: ```javascript var z = 1; var z = 2; // 合法但危险 ``` - **$let$禁止重复声明** 同一作用域内重复声明会报错: ```javascript let w = 1; let w = 2; // SyntaxError ``` #### 四、内存管理差异 $$ \text{内存泄漏风险} = \begin{cases} 高 & \text{使用var时闭包持有共享变量} \\ 低 & \text{使用let时变量随块作用域销毁} \end{cases} $$ #### 最佳实践建议 1. 循环控制变量强制使用$let$ 2. 需要精确控制变量生命周期时使用$let$ 3. 旧代码维护时注意$var$的变量提升特性
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值