3.2 严格模式与 ECMAScript 6

ECMAScript 5 引入严格模式来清理语言,在文件或者函数的第一行放入下面的内容就可以开启严格模式:

'use strict';

严格模式引入了三种破坏性的改变:

  • 语法改变:一些之前合法的语法在严格模式下面是不允许的。例如:

    • 禁止 with 语句。它允许使用者添加任何对象到变量作用域链,这会减缓程序的执行速度,并且很难指出某个变量指向哪里。
    • 删除一个独立的标识符(一个变量,而不是一个属性)是不允许的。
    • 函数只能在作用域的顶层声明。
    • 更多的保留字: implements interface let package private protected public static yield 。
  • 更多种类的错误。例如:

    • 给一个未声明的变量赋值会抛出 ReferenceError 。在非严格模式下,这样干就会创建一个全局变量。
    • 修改只读的属性(比如字符串的长度属性)会抛出 TypeError 。在非严格模式下,不会产生任何效果。
  • 不同的语义:在严格模式下,一些语法结构表现得不一样。例如:

    • arguments 不再随着当前参数值的改变而改变。
    • 在非方法的函数中 thisundefined 。在非严格模式下,它指向全局对象( window ),也就是说如果调用一个构造器的时候没有使用 new ,就会创建一些全局变量。

严格模式是一个很好地说明了版本化是棘手的:即便能够制作一个干净版本的 JavaScript ,也很难被大家接受。主要原因是破坏了一些现存的代码,降低了执行速度,并且加入到文件中也很麻烦(更不用说交互的命令行)。我喜欢严格模式这种想法,但是基本不使用。

3.2.1 支持松散(非严格)模式

一个 JavaScript 意味着我们不能放弃松散模式:此模式将会继续存在(例如在 HTML 属性中)。因此,我们不能基于严格模式来构建 ECMAScript 6 ,必须同时在严格模式和非严格模式(又称为松散模式)下添加特性。否则,严格模式就会成为一个不同的语言版本,就退回了版本化的方式。很不幸,有两个特性很难引入松散模式: let 声明和块级函数声明。让我们看看为什么很难引入和如何引入。

3.2.2 松散模式中的 let 声明

let 使你能够声明块级变量。这很难被引入到松散模式,因为 let 仅在严格模式下是保留字。也就是说,下面两条语句在 ES5 的松散模式下是合法的:

var let = [];
let[x] = 'abc';

在 ECMASCript 6 的严格模式下,第一行就会抛出异常。因为使用了 let 作为变量名。然后第二行会被解析为一个 let 变量声明(使用解构)。

在 ECMAScript 6 的松散模式下,第一行不会抛出异常,但是第二行依然被解析为一个 let 声明。这种使用 let 的方式在 web 上是极少见的,因此 ES6 可以直接这样来解析。 ES5 松散模式下的其他 let 声明的书写方式不会被误解:

let foo = 123;
let {x,y} = computeCoordinates();

3.2.3 松散模式下的块级函数声明

ECMAScript 5 严格模式禁止在块中声明函数,在松散模式下,规范却允许这么做,但是没说这样会发生什么。因此,很多 JavaScript 实现都支持块级函数声明,但是处理方式是不一样的。

ECMAScript 6 想要块中的函数声明本地化(即该函数的作用域救在该块中)。作为 ES5 严格模式的扩展,这是没问题的,但是破坏了一些松散模式的代码。因此, ES6 为浏览器提供了“ web 遗留的兼容语义”,允许块中的函数声明在函数作用域中存在。

3.2.4 其它关键字

标识符 yieldstatic 仅在 ES5 的严格模式下是保留字。 ECMAScript 6 使用上下文相关的语法规则来使它们在松散模式下起作用:

  • 在松散模式下, yield 仅在生成器函数中是保留字。
  • static 现在仅用于类字面量中,类字面中默认就是严格的(见下文)。

3.2.5 隐式的严格模式

在 ECMAScript 6 中,模块体和类体默认就是严格模式的–没必要使用 use strict 标记。考虑到我们所有的代码都将会位于模块中, ECMAScript 6 有效地将整个语言升级到了严格模式。

其它结构体(比如箭头函数和生成器函数)本来也应该隐式地为严格模式。但是考虑到通常情况下这些结构都很小,在非严格模式下使用它们就会造成代码中两种模式的碎片化切换。类,尤其是模块一般是足够大的,这样一来就可以忽略碎片化的代码片段问题了。

3.2.6 无法修复的东西

一个 JavaScript 的缺陷就是无法修复已有的怪异行为,尤其是下面这两个。

第一个, typeof null 应该返回字符串 null 而不是 object 。但是修正这个就会破坏已有的代码。另一方面,给新种类的操作数添加新的操作结果是没问题的,因为当前的 JavaScript 引擎对于一些宿主对象已经会返回自定义的值。 ECMAScript 6 的 Symbol 就是一个例子:

> typeof Symbol.iterator
'symbol'

第二个,全局对象(浏览器中的 window 对象)不应该在变量作用域链。但是现在修正这个也太晚了。但是至少,在模块中不会处于全局作用域下,并且 let 永远不会创建全局对象属性,甚至在全局作用域下使用也不会。

results matching ""

    No results matching ""