let topmost = {} { let inner = {} { let innermost = {} } // attempts to access innermost here would throw } // attempts to access inner here would throw // attempts to access innermost here would throw
在for循环中使用let是一个很好的实践,这样定义的变量只会在当前块作用域内生效。
for (let i = 0; i < 2; i++) { console.log(i) // <- 0 // <- 1 } console.log(i) // <- i is not defined
考虑到let声明的变量在每一次循环的过程中都重复声明,这在处理异步函数时就很有效,不会发生使用var时产生的诡异的结果,我们看一个具体的例子。
我们先看看 var 声明的变量是怎么工作的,下述代码中 i变量 被绑定在 printNumber 函数作用域中,当每个回调函数被调用时,它的值会逐步升到10,但是当每个回调函数运行时(每100us),此时的i的值已经是10了,因此每次打印的结果都是10.
function printNumbers() { for (var i = 0; i < 10; i++) { setTimeout(function () { console.log(i) }, i * 100) } } printNumbers()
使用let,则会把i绑定到每一个块作用域中。每一次循环 i 的值还是在增加,但是每次其实都是创建了一个新的 i ,不同的 i 之间不会相互影响 ,因此打印出的就是预想的0到9了。
function printNumbers() { for (let i = 0; i < 10; i++) { setTimeout(function () { console.log(i) }, i * 100) } } printNumbers()
为了细致的讲述let的工作原理, 我们还需要弄懂一个名为 Temporal Dead Zone 的概念。
Temporal Dead Zone
简言之,如果你的代码类似下面这样,就会报错。即在某个作用域中,在let声明之前调用了let声明的变量,导致的问题就是由于,Temporal Dead Zone(TDZ)的存在。
{ console.log(name) // <- ReferenceError: name is not defined let name = 'Stephen Hawking' }
如果定义的是一个函数,函数中引用了name变量则是可以的,但是这个函数并未在声明前执行则不会报错。如果let声明之前就调用了该函数,同样会导致TDZ。
// 不会报错 function readName() { return name } let name = 'Stephen Hawking' console.log(readName()) // <- 'Stephen Hawking' // 会报错 function readName() { return name } console.log(readName()) // ReferenceError: name is not defined let name = 'Stephen Hawking'
即使像下面这样let定义的变量没有被赋值,下面的代码也会报错,原因依旧是它试图在声明前访问一个被let定义的变量
function readName() { return name } console.log(readName()) // ReferenceError: name is not defined let name
内容版权声明:除非注明,否则皆为本站原创文章。