浅谈JS中的let、const和var的区别以及如何去理解暂时性死区(TDZ)

前言

只要用js的人都应该对let和var的区别有所了解,但是你真的了解吗?通常我们可能知道let不能重复定义,会涉及到代码块的概念。接下来我们通过一些小demo来了解一下两者的区别以及在实际使用let时需要注意的地方。

示例

let a = 10;
console.log(a); // #1
if(true){
    console.log(a); // #2
    let a = 100;
}

根据上面的代码,你能告诉我#2的打印结果吗?如果报告了一个错误,错误信息应该说什么?结果是:未捕获的引用错误:a未定义。是的,你是对的。错误消息说A是未定义的。显然,在一开始,我们已经在全局范围内将A定义为10。如果设a = 100被去掉;在这段代码中,A通常可以得到10。你可能会想,为什么不报错:Uncought语法错误:标识符A已经声明了(A已经声明了)?

暂时性死区

用let声明的变量没有变量提升。而且要求变量只能在let声明语句执行后才能使用,否则会报告未捕获的ReferenceError。

ES6明确规定,如果一个块中有let和const命令,那么这些命令在这个块中声明的变量从一开始就形成了一个封闭的作用域。任何在声明前使用这些变量的人都会报告一个错误。
简而言之,在代码块中,变量在let命令声明之前是不可用的。从语法上来说,这被称为“时间死区”(简称TDZ)。

结论

通过上面的分析,我们可以知道为什么A报告的错误是未定义的。首先,全局定义设a = 10;没问题,然后我通过if做了一个判断。事实上,如果块代码涉及到代码块的概念。然后,我们在if代码块中做了两件事,打印A,重新定义A并将其赋值为100。根据上面的临时死区概念,我们知道只要通过let和const声明了一个变量,整个代码块都会被锁死,如果在块变量let未定义之前使用了该变量,那么一切都会被报为未定义,不管该变量是否全局存在。

TDZ可以做点啥

Let非常适合for循环内部的块级作用域。JS中的for循环体比较特殊,每次执行都是一个全新的、独立的块作用域。let声明的变量传入for循环体的作用域后,不会发生变化,也不受外界影响。看一个常见的面试题目:

for (var i = 0; i <10; i++) {  
  setTimeout(function() {  // 同步注册回调函数到 异步的 宏任务队列。
    console.log(i);        // 执行此代码时,同步代码for循环已经执行完成
  }, 0);
}
// 输出结果
10...   共10个10
// 这里面的知识点: JS的事件循环机制,setTimeout的机制等

如果把var改成let声明:

// i虽然在全局作用域声明,但是在for循环体局部作用域中使用的时候,变量会被锁定,不受外界干扰。
for (let i = 0; i < 10; i++) {
  setTimeout(function() {
    console.log(i);    //  i 是循环体内局部作用域,不受外界影响。
  }, 0);
}
// 输出结果:
0  1  2  3  4  5  6  7  8 9

小结

ES6的let有一个很好的代码块范围的概念,这也为我们的代码提供了一个标准,不像以前的“var”字段,数据丢失要花半天时间。但是对于代码风格不是很严谨的人来说可能有点繁琐,因为以前很多人都是自由发挥写代码。请慢慢改正。

—- 本页内容已结束,请看下一页内容—-

感谢您的来访,获取更多精彩文章请收藏本站。

© 版权声明
THE END
喜欢就支持一下吧
点赞13赞赏 分享
评论 共2条

请登录后发表评论