一、问题

ES6中引入letconst来进行块级作用域,这个应该是个使用es6的人都知道。

不过我一直不明白为什么 letconst能够实现或者做到块级作用域,而不是变量声明提升.

插曲 javascript 中,es5中(非严格模式),var或者function只会声明提升,因此在声明赋值之前进行typeof value的时候,还是undefined.

在看zakas的新书的时候,才真正明白为什么能够实现不进行变量声明提升.

二、解释 (TDZ)

zakas 的新书中使用了 javascript 社区的一个词语:TDZ(temporal dead zone).

  • 临时死区

ECMASCRIPT标准并没有明确指出TDZ,我特意去查了一下,这个词是社区的,但是标准好像没有明确说是什么东西。

基本的思想是:

javascript 引擎在扫描代码发现变量声明的时候,如果遇到了 var 声明,则自然的将其提升到当前作用域(执行环境)的顶部,但是当遇到letconst的时候,会将他们放在临时死区中。

如果在 let 和 const 声明之前访问变量,则会访问 TDZ 中的变量,会触发运行时错误。

本身 TDZ 中的变量,只有在执行了声明之后,才会从 TDZ 中移出,然后才能正常访问。

三、需要注意的点

有一点比较重要的事,变量提升控制的是当前作用域或者执行环境,因此某个TDZ也只负责某个执行环境(这里的某个并不是说TDZ有很多)。

一个典型的例子:

console.log(typeof value); // undefined

if(true){
   console.log(typeof value); // 抛出错误
   const value = 1;
}

上面代码中,能够发现,if外访问 value 不会报错,因为 if 外 typeof value时变量还没有在TDZ中,所以是undefined。

四、全局作用域的绑定

ES5中,如果在全局中声明了name,则其回自动的挂在window的属性上,而 window 本身也有一个 name 属性,并且浏览器对这个属性的解析是和其他属性不同的。

以下面代码举例:

var name = {
    first: "ptbird",
    last: "postbird"
},
name2 = {
    first: "ptbird",
    last: "postbird"
};
console.log(name);
console.log(name2);

理论上两者是一样的,但是实际上在输出的时候会发现两者的不同:

1.png

因为 name 是覆盖了window.name,而对这个属性的解析由于其他属性不一样,因此出现这种情况,所以基本上是不推荐使用name进行全局变量的声明。

在es6中,let 和 const 声明的变量则不会自动的挂在window的属性上。

如下所示:

let name = {
    first: "ptbird",
    last: "postbird"
};
console.log(name);
console.log(window.name);

2.png

一开始我也不明白为什么能够做到这一点,不过 zakas 也有指出,实际上let生命的时候,是创建了一个遮蔽window同名属性的全局变量.

关键点是 遮蔽,因此实际上使用 window.namename 现在是两个不同的变量, name 将 widnow.name 给遮蔽了。

所以,在es6的代码中,我基本上都是用const或者是let而很少用var,除非特殊需要才会使用var。

es6.jpg