let
-
块级作用域
{ let a = 1; var b = 2; } a; //ReferenceError b; //2
-
不存在变量提升
console.log(child); //ReferenceError let child = 'child';
-
暂时性死区(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 也不再安全.只要变量未进行声明,那么使用的时候就会报错. //变量先声明,后使用. }
-
不允许重复声明
let 不允许在相同的作用域内重复声明同一个变量.
const
- const 声明一个只读的常量.一旦声明,常量的值就不能改变.
TypeError: Assignment to constant variable.
- const 只声明不赋值,就会报错.
SyntaxError: Missing initializer in const declaration
- 只在所声明的块级作用域内有效
- 不存在变量提升
- 暂时性死区
- 不可重复声明
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语言最大的设计败笔之一.这样的设计带来几个问题.
- 首先是没法再编译时候就报出变量未声明的错误,只有运行时候才能够知道(因为全局变量可能是顶层对象的属性创造的,而属性的创造是动态的);
- 其次,容易创造出全局变量.
- 顶层对象的属性是到处可以读写的.这非常不利于模块化.
ES6 为了改变这一点,一方面规定,为了保证兼容性,var命令和function命令 声明的全局变量,依旧是顶层对象的属性;另一方面规定,let命令,const命令,class命令生命的全局变量,不属于顶层的属性.也就是说,从ES6 开始,全局变量将逐步与顶层对象的属性脱钩.
global对象
ES5 的顶层对象,本身也是一个问题. 因为它在各种实现里面是不统一的.
- 浏览器中,顶层对象是window,但Node和Web Worker 没有window.
- 浏览器和Web Worker里面,self 指向的是顶层对象,但是 Node 没有self.
- Node 里面,顶层对象是global ,但是其他环境都不支持.
同一段代码为了能够在各种环境中,都能取到顶层对象,现在一般使用this对象,但是在Node模块和 Es6 模块中,this返回的是当前模块.
函数里面的this,如果函数不是作为对象的方法运行,而是单纯的作为函数运行,this 会指向顶层对象.但是严格模式下,this会返回undefined.
新提案,在语言标准层面,引入global作为顶层对象,也就是说在所有环境下,global都是存在的,都可以从它拿到顶层对象.(垫片库system.global模拟了这个提案,可以在所有环境下拿到global)