1. 块级作用域
在es6以前写代码, 通常二话不说, 先写一段(function(){})()
类似这种的匿名自调函数. 但是现在想要让变量不污染全局环境, 可以有更简单的写法.
{
var a = 1;
let b = 1;
}
console.log(window.a); // 1
console.log(window.b); // undefined
这是因为在ES6语法中支持块级作用域, 使用{}来声明一个块级作用域.
并且块级作用域可以嵌套, 同时子级可以访问父级的作用域, 但是父级不能访问子级:
{
let a = 1;
{
console.log(a);
console.log(b); // Uncaught ReferenceError: b is not defined
{
let b = 2;
}
}
}
值得注意的一点是, if/for循环中()内使用let或者const声明的变量属于后面的{}, 而不是{}外部:
{
console.log(i); // Uncaught ReferenceError: i is not defined
for (let i = 0; i < 3; i++) {
console.log(i);
}
}
2.暂存性死区
在相同的函数或块作用域内重新声明同一个变量会引发SyntaxError; 在声明变量或常量之前使用它, 会引发ReferenceError. 这就是暂存性死区.
这里主要存在两个坑点:
- switch case中case语句的作用域
switch (x) {
case 0:
let foo;
break;
case 1:
let foo; // TypeError for redeclaration.
break;
}
会报错是因为switch中只存在一个块级作用域, 改成以下形式可以避免:
let x = 1;
switch(x) {
case 0: {
let foo;
break;
}
case 1: {
let foo;
break;
}
}
- 与词法作用域结合的暂存死区
function test(){
var foo = 33;
if (true) {
let foo = (foo + 55); // ReferenceError
}
}
test();
在if语句中使用了let声明了foo, 因此在(foo+55)中引用的是if块级作用域中的foo, 而不是test函数中的foo; 但是由于if中的foo还没有声明完:
在这一行中,if块的“foo”已经在词法环境中创建,但尚未达到(并终止)其初始化(这是语句本身的一部分)
因此它仍处于暂存死区.
下面的例子也是同样的问题:
function go(n) {
// n here is defined!
console.log(n); // Object {a: [1,2,3]}
for (let n of n.a) { // ReferenceError: n is not defined
console.log(n);
}
}
go({a: [1, 2, 3]});
指令let n of n.a已经在for循环块的私有范围内,因此标识符“n.a”被解析为位于指令本身的第一部分(“let n”)中的'n'对象的属性'a' ,由于尚未达成和终止其声明,因此仍处于暂存死区。