ES6 let声明之变量提升,块级作用域,计数器变量,函数声明

在ES5中,只存在全局作用域和函数作用域。而且var声明存在变量提升的问题。这是非常不合理的。

ES6 let声明之变量提升,块级作用域,计数器变量,函数声明

 这个函数会输出什么?test? hello world? 不不不,是undefined。因为ES5中没有块级作用域,而且存在变量提升的问题。这个函数实际上的执行如下代码

ES6 let声明之变量提升,块级作用域,计数器变量,函数声明

这是由于变量提升导致的内层变量覆盖了外层的变量。 

 什么是变量提升?

变量可以在声明之前使用,值为undefined。这就是变量提升。

ES6 let声明之变量提升,块级作用域,计数器变量,函数声明

let声明不存在变量提升:let 声明的变量必须要在声明之后使用,否则就会报错。

let声明不存在变量提升的原理:暂时性死区

只要作用域中存在let声明,那么这个变量就会绑定这个区域。同时,这个区域会变成一个块级作用域。只要这个区域中存在let声明,那么let声明的变量就会被存放在暂时性死区(TDZ)中,直到这个变量被声明,才会被拿出来。使用TDZ中的变量会报错。

ES6 let声明之变量提升,块级作用域,计数器变量,函数声明

块级作用域

for循环中使用var声明产生的问题:1.计数变量泄露成全局变量,2.计数变量共用

1.

ES6 let声明之变量提升,块级作用域,计数器变量,函数声明

 2.

ES6 let声明之变量提升,块级作用域,计数器变量,函数声明

(这里面牵扯到异步函数的执行顺序问题,在这里简单介绍下就是:setTimeout是个异步函数,使得console.log(i)在最后才被调用,这个时候,for循环结束,同时执行了一次i++,这个时候 i =5 全局只存在一个公用 i ,所以输出5个5)。

使用let作为计数变量是完美的。

 ES6 let声明之变量提升,块级作用域,计数器变量,函数声明

 使用let声明后,i 只在本轮循环有效,相当于每一轮都会重新声明一个 i。而且JS引擎会记住上一轮的 i。

ES6 let声明之变量提升,块级作用域,计数器变量,函数声明

所以,尽情使用let 到你的for循环中吧。 

块级作用域中的函数声明

来看这样一个函数声明

ES6 let声明之变量提升,块级作用域,计数器变量,函数声明

在ES5环境下,运行的输出 “insede”.  ES5实际执行的代码如下 

ES6 let声明之变量提升,块级作用域,计数器变量,函数声明

而在ES6环境下执行的是这样的:

ES6 let声明之变量提升,块级作用域,计数器变量,函数声明 

 阮一峰老师总结的很好:在ES6中

1.允许在块级作用域中声明函数

2.函数声明类似于var,会提升函数声明到全局作用域或者函数作用域头部。(函数声明并不是类似于let,因为这样的话会对旧版代码产生很大影响,为了减轻兼容性问题。)

3.函数声明还会提升到所在块级作用域头部

总的来说,因为存在很大兼容性问题,应该减少使用块级作用域中声明函数。或者使用函数表达式形式,而不是函数声明语句。