由块级作用域引出的一场变革

块级作用域又称词法作用域,存在于:

  • 函数内部(函数作用域)

  • 块中(字符 { 和 } 之间的区域)

注意:ES6允许块级作用域任意嵌套

{{{{{{let text = 'Hello World!'}}}}}}

因为有了块级作用域,然后我们才有继续往下聊的可能。

1、 块级声明

块级声明是用于声明在指定块的作用域之外无法访问的变量。

2、 let声明:用来声明一个块级作用域变量

  1. 声明的变量具有块级作用域的特性

// 例子
function getValue (condition) {
    if (condition) {
        let value = 'blue';
        return value;
    }
    console.log(value)
    // 报错 value is not defined
}
getValue()
  1. 在同一个作用域内不能使用let声明同名的变量

  1. 声明没有预解析,不存在变量提升,有“临时死区”(TDZ)

从块的开始到变量声明这段的区域被称为临时死区,ES6明确规定,如果区块中存在let和const命令,则这个区块对这些命令声明的变量从一开始就形成封闭作用域,只要在声明之前就使用这些变量(赋值,引用等等),就会报错。

注意:TDZ是区域是“块开始”到“变量声明”,下面的例子不报错

3、 const声明:声明常量(如PI),值一旦被设定后不可更改

  1. 常量声明的值是不可变的

注意:const声明的对象不允许修改绑定,但可以修改该对象的属性值。

  1. 因为常量声明后值就不可更改了,所以声明时必须赋值

  1. 声明的常量具有块级作用域的特性

  1. 在同一个作用域内不能使用const声明同名的变量

  1. 声明没有预解析,不存在变量提升,有“临时死区”(TDZ)

总结:一张表格

声明方式

变量提升

作用域

是否需要初始值

重复定义

var

函数级

不需要

允许

let

块级

不需要

不允许

const

块级

需要

不允许

扩展:再提一下变量命名,不管是var、let、const声明的变量名,可以由数字,字母,下划线及美元符号组成,但是不能以数字开头。美元符号可以放到任何一个位置,甚至单独一个美元符号。

4、 循环中的块作用域绑定

循环中的let声明

注意:有一点很重要,let 声明在循环内部的行为是标准中专门定义的,它不一定与 let 不提升特性有关。

循环中的const声明

注意:const可以应用在 for-in 和 for-of 循环中,是因为每次迭代不会修改已有绑定,而是会创建一个新绑定。

5、 块级绑定最佳实践的进化

ES6 早期

普遍认为默认使用let来替代var,对于写保护的变量使用const

ES6 使用中

普遍默认使用const,只有确实需要改变变量的值时使用let。因为大部分变量的值在初始化后不应再改变,而预料之外的变量值的改变是许多bug的源头。这样就可以在某种程度上实现代码的不可变,从而防止某些错误的发生。

6、 全局变量将逐步与顶层对象的属性脱钩

顶层对象,在浏览器环境指的是window对象,在Node指的是global对象。

为了保持兼容性,var命令和function命令声明的全局变量,依旧是顶层对象的属性;

var 声明的a,在右侧 global 里面

另一方面规定,let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性。

上图可见let 声明的变量,并没有在Window对象里,而是一个新的Script对象。

扩展:如果需要在浏览器中跨frame或window访问代码,仍然可以用var在全局对象下定义变量。

7、 块级函数

从ECMAScript 6开始,在严格模式下,块里的函数作用域为这个块。ECMAScript 6之前不建议块级函数在严格模式下使用。

注意:在非严格模式下不要用块级函数,因为在非严格模式下,块中函数的声明表现奇怪,有兼容性风险

ECMAScript 6中,如果shouldDefineZero是false,则永远不会定义zero,因为这个块不执行。这是新标准定义的。然而,这里存在历史遗留问题,无论这个块是否执行,一些浏览器会定义zero。

在严格模式下,所有支持ECMAScript 6的浏览器以相同的方式处理:只有在shouldDefineZero为true的情况下定义zero,并且作用域只是这个块内。

Last updated

Was this helpful?