let & const

let

  1. 块级作用域

    {
        let a = 1;
        var b = 2;
    }
    a; //ReferenceError
    b; //2
  1. 不存在变量提升

    console.log(child); //ReferenceError
    let child = 'child';    
  1. 暂时性死区(TDZ:temporal dead zone)

    只要块级作用域中存在let 命令,它所声明的变量就绑定这个区域,不再受外部影响.

    if(true){
        //  TDZ 开始;
        temp = 'abc';
        console.log(tmp);//ReferenceError;
        let tmp;
        //TDZ 结束;
        console.log(tmp);//undefined;
        temp = 'temp';
        console.log(temp);//temp;
        //上述代码块级作用域中在使用let命令声明变量temp之前,都属于tmp的死区.
        //暂时性死区意味着 typeof 也不再安全.只要变量未进行声明,那么使用的时候就会报错.
        //变量先声明,后使用.
    
    }
  1. 不允许重复声明

    let 不允许在相同的作用域内重复声明同一个变量.

const

  1. const 声明一个只读的常量.一旦声明,常量的值就不能改变.TypeError: Assignment to constant variable.
  2. const 只声明不赋值,就会报错.SyntaxError: Missing initializer in const declaration
  3. 只在所声明的块级作用域内有效
  4. 不存在变量提升
  5. 暂时性死区
  6. 不可重复声明

const 实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址不得改动.
对于简单的数据类型(数值,字符串,布尔值),值就保存在变量指向的那个内存地址中因此等同于常量.但是将一个复合类型的数据(主要是对象和数组)变量指向的内存地址,保存的只是一个指针,const 只能保证这个指针是固定的,至于它指向的数据结构是不是可变的,就完全不能控制了.

const foo = {};
//为 foo 添加一个属性,可以成功.
foo.prop = '123';
console.log(foo.prop);//123

//将foo 指向另一个对象,就会报错.
foo = {};//TypeError: Assignment to constant variable.    

ES6 声明变量的六种方法

ES5 只有两种声明变量的方法: var 命令和 function 命令.ES6 除了let 和 const 命令. 还会有import 和class 命令.

顶层对象的属性

顶层对象,在浏览器环境中指的是 window对象,在Node 指的是global对象. 在ES5中,顶层对象的属性与全局变量是等价的.

window.a = 1;
a;//1

a=2;
window.a;//2

顶层对象被设计来与全局变量挂钩,被认为是javascript语言最大的设计败笔之一.这样的设计带来几个问题.

  1. 首先是没法再编译时候就报出变量未声明的错误,只有运行时候才能够知道(因为全局变量可能是顶层对象的属性创造的,而属性的创造是动态的);
  2. 其次,容易创造出全局变量.
  3. 顶层对象的属性是到处可以读写的.这非常不利于模块化.

ES6 为了改变这一点,一方面规定,为了保证兼容性,var命令和function命令 声明的全局变量,依旧是顶层对象的属性;另一方面规定,let命令,const命令,class命令生命的全局变量,不属于顶层的属性.也就是说,从ES6 开始,全局变量将逐步与顶层对象的属性脱钩.

global对象

ES5 的顶层对象,本身也是一个问题. 因为它在各种实现里面是不统一的.

  1. 浏览器中,顶层对象是window,但Node和Web Worker 没有window.
  2. 浏览器和Web Worker里面,self 指向的是顶层对象,但是 Node 没有self.
  3. Node 里面,顶层对象是global ,但是其他环境都不支持.

同一段代码为了能够在各种环境中,都能取到顶层对象,现在一般使用this对象,但是在Node模块和 Es6 模块中,this返回的是当前模块.
函数里面的this,如果函数不是作为对象的方法运行,而是单纯的作为函数运行,this 会指向顶层对象.但是严格模式下,this会返回undefined.

新提案,在语言标准层面,引入global作为顶层对象,也就是说在所有环境下,global都是存在的,都可以从它拿到顶层对象.(垫片库system.global模拟了这个提案,可以在所有环境下拿到global)