块级作用域和暂存性死区

2018-04-21  本文已影响0人  wannaBeACat

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. 这就是暂存性死区.
这里主要存在两个坑点:

  1. 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;
  }
}
  1. 与词法作用域结合的暂存死区
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' ,由于尚未达成和终止其声明,因此仍处于暂存死区。

上一篇 下一篇

猜你喜欢

热点阅读