Skip to main content

[JS] 严格模式

摘记<Javascript高级程序设计> -- 马特 2020版 附录

严格模式

ECMAScript 5 首次引入严格模式的概念. 严格模式用于选择以更严格的条件检查 JavaScript 代码错误,可以应用到全局,也可以应用到函数内部。严格模式的好处是可以提早发现错误,因此可以捕获某些 ECMAScript 问题导致的编程错误。

理解严格模式的规则非常重要,因为未来的 ECMAScript 会逐步强制全局使用严格模式。严格模式已得到所有主流浏览器支持。

模式开启

要选择使用严格模式,需要使用严格模式编译指示(pragma),即一个不赋值给任何变量的字符串:

"use strict"; 

这样一个即使在 ECMAScript 3 中也有效的字符串,可以兼容不支持严格模式的 JavaScript 引擎。支持严格模式的引擎会启用严格模式,而不支持的引擎则会将这个编译指示当成一个未赋值的字符串字面量。

如果把这个编译指示应用到全局作用域,即函数外部,则整个脚本都会按照严格模式来解析。这意味着在最终会与其他脚本拼接为一个文件的脚本中添加了编译指示,会将该文件中的所有 JavaScript 置于严格模式之下。

也可以像下面这样只在一个函数内部开启严格模式

function doSomething() {
"use strict";
// 其他代码
}

如果你不能控制页面中的所有脚本,那么建议只在经过测试的特定函数中启用严格模式。

变量

严格模式下如何创建变量及何时会创建变量都会发生变化。第一个变化是不允许意外创建全局变量。在非严格模式下,以下代码可以创建全局变量:

// 变量未声明
// 非严格模式:创建全局变量
// 严格模式:抛出 ReferenceError
message = "Hello world!";

虽然这里的 message 没有前置 let 关键字,也没有明确定义为全局对象的属性 ,但仍然会自动创建为全局变量。在严格模式下,给未声明的变量赋值会在执行代码时抛出 ReferenceError。

相关的另一个变化是无法在变量上调用 delete。在非严格模式下允许这样,但可能会静默失败(返 false)。在严格模式下,尝试删除变量会导致错误:

// 删除变量
// 非严格模式:静默失败
// 严格模式:抛出 ReferenceError
let color = "red";
delete color;

严格模式也对变量名增加了限制。具体来说,不允许变量名为 implements、interface、let、package、private、protected、public、static 和 yield。这些是目前的保留字,可能在将来的 ECMAScript 版本中用到。如果在严格模式下使用这些名称作为变量名,则会导致语法错误

对象

在严格模式下操作对象比在非严格模式下更容易抛出错误。严格模式倾向于在非严格模式下会静默失败的情况下抛出错误,增加了开发中提前发现错误的可能性。

首先,以下几种情况下试图操纵对象属性会引发错误。

  • 给只读属性赋值会抛出 TypeError。
  • 在不可配置属性上使用 delete 会抛出 TypeError。
  • 给不存在的对象添加属性会抛出 TypeError。

this 强制转型

JavaScript 中最大的一个安全问题,也是最令人困惑的一个问题,就是在某些情况下 this 的值是如何确定的。使用函数的 apply()或 call()方法时,在非严格模式下 null 或 undefined 值会被强制转型为全局对象。在严格模式下,则始终以指定值作为函数 this 的值,无论指定的是什么值。例如:

// 访问属性
// 非严格模式:访问全局属性
// 严格模式:抛出错误,因为 this 值为 null
let color = "red";
function displayColor() {
alert(this.color);
}
displayColor.call(null);

这里在调用 displayColor.call()时传入 null 作为 this 的值,在非严格模式下该函数的 this值是全局对象。结果会显示"red"。在严格模式下,该函数的 this 值是 null,因此在访问 null 的属性时会抛出错误。

通常,函数会将其 this 的值转型为一种对象类型,这种行为经常被称为“装箱”(boxing)。这意味着原始值会转型为它们的包装对象类型。

function foo() {
console.log(this);
}
foo.call(); // Window {}
foo.call(2); // Number {2}

在严格模式下执行以上代码时,this 的值不会再“装箱”:

function foo() {
"use strict";
console.log(this);
}

foo.call(); // undefined
foo.call(2); // 2

类与模块

类和模块都是 ECMAScript 6 新增的代码容器特性。在之前的 ECMAScript 版本中没有类和模块这两个概念,因此不用考虑从语法上兼容之前的 ECMAScript 版本。为此,TC39 委员会决定在 ES6 类和模块中定义的所有代码默认都处于严格模式。