• Promise承诺。承诺一件事,会立刻去执行,但是执行周期不确定,执行周期结束后执行结果:成功和失败不确定。
    • 暂时性死区。 Let、Const命令声明 Tmp之前,Tmp是不能使用的,会报错,是属于 Tmp 的暂时性死区。 Var命令声明 Tmp之前是可以使用的,返回 Undefined未定义。
    1. if (true) {
    2. // TDZ开始
    3. tmp = 'abc'; // 报错ReferenceError
    4. console.log(tmp); // ReferenceError
    5. let tmp; // TDZ结束
    6. console.log(tmp); // undefined
    7. tmp = 123;
    8. console.log(tmp); // 123
    9. }

    有些“死区”比较隐蔽,不太容易发现。

    1. function bar(x = y, y = 2) {
    2. return [x, y];
    3. }
    4. bar(); // 报错

    上面代码中,调用 Bar函数之所以报错(某些实现可能不报错),是因为参数 X默认值等于另一个参数 Y,而此时 Y还没有声明,属于“死区”。如果 Y的默认值是 X,就不会报错,因为此时 X已经声明了。

    1. function bar(x = 2, y = x) {
    2. return [x, y];
    3. }
    4. bar(); // [2, 2]

    另外,下面的代码也会报错,与 Var的行为不同。

    1. // 不报错
    2. var x = x;
    3. // 报错
    4. let x = x;
    5. // ReferenceError: x is not defined

    上面代码报错,也是因为暂时性死区。使用 Let声明变量时,只要变量在还没有声明完成前使用,就会报错。上面这行就属于这个情况,在变量 X的声明语句还没有执行完成前,就去取 X的值,导致报错”x 未定义“。

    • ES6在5的基础上增加了块作用域,原因在于没有块作用域的时候发生的两种情况:内部变量覆盖了外部变量,循环变量泄漏成全局变量。 内层变量可能会覆盖外层变量。
      1. var tmp = new Date();
      2. function f() {
      3. console.log(tmp);
      4. if (false) {
      5. var tmp = 'hello world';
      6. }
      7. }
      8. f(); // undefined

    代码块的外部使用外层的 Tmp变量,内部使用内层的 Tmp变量。但是,函数f执行后,输出结果为 Undefined,原因在于变量提升,导致内层的 Tmp变量覆盖了外层的 Tmp变量。
    (2)用来计数的循环变量泄露为全局变量。

    1. var s = 'hello';
    2. for (var i = 0; i < s.length; i++) {
    3. console.log(s[i]);
    4. }
    5. console.log(i); // 5

    变量 I只用来控制循环,但是循环结束后并没有消失,泄露成了全局变量。
    作用域 内层变量可以定义和外层变量同名的变量,反之报错。函数可以在块级作用域内声明,在块级作用域内声明的函数在块级作用域外不能引用。

    • Const 声明一个只读的常量,一旦声明,常量的值不能改变。如果想赋值,需要在声明后立即初始化。实际上,不是变量的值不能改变,是变量所指向的内存地址的数据不能改变。 对于简单的数据(数值,字符串和布尔值等),Const是指向内存地址的数据,变量约等于常量。对于复合的数据(对象和数组等),保存的是指向实际数据的指针,Const只能保证这个指针是固定的,但指针指向但数据结构不可控。 将对象冻结,应该使用 Object.Freezee.
    • 只要某种数据结构具有 Iterator 接口,都可以采用数组形式的解构赋值 之所以会报错,是因为 X用 Y做默认值时, Y还没有声明。
    1. function* fibs() {
    2. let a = 0;
    3. let b = 1;
    4. while (true) {
    5. yield a;
    6. [a, b] = [b, a + b];
    7. }
    8. }
    9. let [first, second, third, fourth, fifth, sixth] = fibs();
    10. sixth // 5
    1. let [x = 1, y = x] = []; // x=1; y=1
    2. let [x = 1, y = x] = [2]; // x=2; y=2
    3. let [x = 1, y = x] = [1, 2]; // x=1; y=2
    4. let [x = y, y = 1] = []; // ReferenceError: y is not defined

    可以使用圆括号的情况只有一种:赋值语句的非模式部分,可以使用圆括号。

    1. [(b)] = [3]; // 正确
    2. ({ p: (d) } = {}); // 正确
    3. [(parseInt.prop)] = [3]; // 正确

    都可以正确执行,因为首先它们都是赋值语句,而不是声明语句;其次它们的圆括号都不属于模式的一部分。第一行语句中,模式是取数组的第一个成员,跟圆括号无关;第二行语句中,模式是p,而不是d;第三行语句与第一行语句的性质一致。