【前端工程师手册】JavaScript作用域拾遗
昨天总结了一些作用域的知识【前端工程师手册】JavaScript之作用域,但是发表完发现忘记了一些东西,今天拾个遗。
昨天说到了JavaScript中没有块级作用域,其实在es6中是有的。
es6中的块级作用域
先举个栗子:
var foo = true;
if (foo) {let bar = foo * 2;bar = something( bar ); console.log(bar);}console.log( bar ); // ReferenceError
这个是let最直观的作用,在一对大括号中创建了块级作用域,bar会在大括号中的代码执行完毕后销毁。
再举个栗子:
for(var i = 1;i <= 5;i++) {setTimeout(function() {console.log(i)}, i*1000)
}
// 每隔一秒打印一个6,共打印5次
如果说这段代码的初衷是间隔1秒打印出1、2、3、4、5的话,结果是令人大跌眼镜的,真正的结果是每隔1秒打印一次6,打印5次.
为什么会这样子?首先是因为闭包的原因,闭包后面再说,现在先理解为闭包是一个函数,一个能够访问并未在它自己内部定义的变量的函数。
OK,接下来说深层次原因。for循环完毕之后,i=6,且此时生成了5个匿名函数 function(){ console.log(i) },由于这5个匿名函数处在同一个词法作用域中,所以他们引用同一个i,所以当他们执行时,自然而然就会打出6。
如何解决?
for(let i = 1;i <= 5;i++) {setTimeout(function() {console.log(i)}, i*1000)
}
// 间隔一秒分别打印出1、2、3、4、5
把var换成let声明就可以了。
《你不知道的JavaScript-上卷》中解释道:
for 循环头部的 let 不仅将 i 绑定到了 for 循环的块中,事实上它将其重新绑定到了循环的每一个迭代中,确保使用上一个循环迭代结束时的值重新进行赋值。
说白了就是再每次迭代内部,都会对 i 进行隐形的重新赋值,且使用的是上一个迭代结束时的值来对 i 进行重新赋值。
差不多就是这样的:
for(let i = 1;i <= 5;i++) {let i = 上次迭代结束的isetTimeout(function() {console.log(i)}, i*1000)
}
所以5个匿名函数引用的并不是同一个i,自然就会顺利的间隔一秒分别打印出1、2、3、4、5了