1. for无块级作用域
var a = []
for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i)
}
}
a[6]() // 10
执行结果:10
结果分析:由于JS没有块级作用域,所以for中变量i,由于使用var声明的故会被添加到最近的环境中,在这里就是全局环境.
每一次循环,新的i值就会覆盖旧值.所以全局i保存的是最后一轮循环后的值10.
由于a[i]的值是一个函数,在函数内部是一个新的作用域,执行a[6]()
会现在当前a[6]()
的函数作用域内部查找i,找不到便会去父作用域查找,这里它的父作用域即全局作用域.所以输出10
作用域链如下:
全局作用域window: a i
函数a[i]()
内部作用域:
2. let 构造块级作用域
var a = []
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i)
}
}
a[6]() // 6
console.log(i) // 报错 ReferenceError: i is not defined
执行结果:6
结果分析:
ES6提供新增let命令,其所声明的变量只在let命令所在的代码块内有效.
for中的变量i用let定义的,当前的i只在本轮循环有效.所以每次的i都是一个新的变量.
报错的原因是当前是全局作用域无法访问到内部变量
作用域链如下:
全局作用域window: a
块级作用域for: i
函数a[i]()
内部作用域:
3. 关于let补充
- 不存在变量提升
let不像var会发生"变量提升"现象.所以,变量一定要在声明后使用.如果在之前使用typeof也会报错
console.log(a)
let a = 0
// 报错 ReferenceError: a is not defined
- 不允许重复声明
let不允许在相同作用域内重复声明同一个变量
function () {
let a = 10
var a = 1
} // SyntaxError: Unexpected token (
-
暂时性死区
只要块级作用域中存在let命令,它所声明的变量就绑定这个区域,不再受外部的影响.
var tmp = 123
if (true) {
tmp = 'abc'
let tmp
}
// ReferenceError: tmp is not defined
报错原因:
存在全局变量tmp,但是块级作用域内let又声明了一个局部变量tmp,导致后者绑定这个块级作用域,所以在let声明变量前,对tmp赋值会报错.
ES6规定:如果区块中存在let和const命令,则这个区块对这些命令声明的变量从一开始就形成封闭作用域.只要在声明之前使用这些变量,就会报错.
在代码块内,使用let命令声明变量之前,该变量都是不可用的.这种语法上称"暂时性死区Temporal dead zone,简称TDZ"
有些死区比较隐蔽,不容易被发现
function bar(x = y,y = 2) {
return [x, y]
}
bar(); // SyntaxError: Invalid or unexpected token
报错原因:参数x默认值等于另一个参数y,而此时y还没有声明,属于"死区" 如果修改为 function bar(x = 2,y = x)
就不会报错
for循环的计数器,很适合用let命令
思考题一
var Test = {
foo: 'test',
func: function () {
var self = this;
console.log(this.foo); // test
console.log(self.foo); // test
(function () {
console.log(this.foo); // undefined
console.log(self.foo); // test
})();
}
}
Test.func();
function test() {
for(let i = 0; i < 5;i++) {
setTimeout(function () {
console.log(i); // 0,1,2,3,4
},1000);
}
}
test();
function test() {
for(var i = 0; i < 5;i++) {
setTimeout(function () {
console.log(i); // 5,5,5,5,5
},1000);
}
}
test();
思考题二
- let暂时性死区和块级作用域
网友评论