那到底let和var有什么不同呢?非常高兴你提出这个问题!
这一规则可以帮助你捕捉bug,除了NaN错误以外,每一个异常都会在当前行抛出。
let声明的变量拥有块级作用域。也就是说用let声明的变量的作用域只是外层块,而不是整个外层函数。
let声明仍然保留了提升的特性,但不会盲目提升。在runTowerExperiment这个示例中,通过将var替换为let可以快速修复问题,如果你处处使用let进行声明,就不会遇到类似的bug。
let声明的全局变量不是全局对象的属性。这就意味着,你不可 以通过window.变量名的方式访问这些变量。面向对象 面向过程的区别它们只存在于一个不可见的块的作用域中,这个块理论上是Web页面中运行的所有JS代码的外层块。
形如for (let x...)的循环在每次迭代时都为x创建新的绑定。
这是一个非常微妙的区别,拿我们的会说话的猫的例子来说,如果一个for (let...)循环执行多次并且循环保持了一个闭包,那么每个闭包将捕捉一个循环变量的不同值作为副本,而不是所有闭包都捕捉循环变量的同一个值。
所以在会说话的猫示例中,也可以通过将var替换为let修复bug。
这种情况适用于现有的三种循环方式:for-of、for-in、以及传统的用分号分隔的类C循环。
let声明的变量直到控制流到达该变量被定义的代码行时才会被装载,所以在到达之前使用该变量会触发错误。举个例子:
不可访问的这段时间变量一直处于作用域中,但是尚未装载,它们位于临时死区(Temporal Dead Zone,简称TDZ)中。我一直想用科幻小说来类比这个脑洞大开的行话,但是还没想好怎么搞。
(脆弱的性能细节:在大多数情况下,查看代码就可以区分声明是否已经执行,所以事实上,JavaScript引擎不需要在每次代码运行时都额外执行 一次变量可访问检查来确保变量已经被初始化。然而在闭包内部有时不是透明的,这时JavaScript引擎将会做一个运行时检查,也就意味着let相对var而言比较慢。)
(脆弱的平行宇宙作用域细节:在一些编程语言中,一个变量的作用域始于声明之处,而非前后覆盖整个封闭代码块。标准委员会曾考虑过将这种作用域准则赋予let关键词,但是一旦使用这种准则,原本提前使用变量的语句会导致引用错误(ReferenceError),现在该语句不位于let t的声明作用域中,根本不会引用此处的变量t,而是引用外层作用域的相应变量。但是这个方法无法与闭包和函数提升很好得结合,所以该提案最终被否决了。)
用let重定义变量会抛出一个语法错误(SyntaxError)。
这一条规则也可以帮助你检测琐碎的小问题。诚然,这亦是var与let的不同之处,当你全局搜索var替换为let时也会导致let重定义语法错误,因为这一规则对全局let变量也有效。
如果你的多个脚本中都声明了相同的全局变量,你最好继续用var声明这些变量。如果你换用了let,后加载的脚本都会执行失败并抛出错误。
或者你可以考虑使用ES6内建的模块机制,后面的文章中会详细讲解。
(脆弱的语法细节:let是一个严格模式下的保留词。在非严格模式下,出于向后兼容的目的,你仍可以用let命名来声明变量、函数和参数,虽然你不会犯傻,但是你确实可以编写var let = 'q';这样的代码!不过let let;无论如何都是非法的。)
在那些不同之外,let和var几乎很相似了。举个例子,它们都支持使用逗号分隔声明多重变量,它们也都支持 解构 特性。
注意,class类声明的行为与var不同而与let一致。如果你加载一段包含同名类的脚本,后定义的类会抛出重定义错误。
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-85542-4.html
是日本的克星
找个退役船撞它