Menu

JavaScript 严格模式:'use strict' 到底做了什么

JavaScript 严格模式改变了什么,如何开启,以及为什么现代代码(模块和类)会自动启用它。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

严格模式是 JavaScript 更严格的变体

JavaScript 历史悠久,也背负着许多值得商榷的设计:会悄悄创建全局变量的赋值,含义随调用方式而变的 this,以及重复的参数名居然也能……正常运行。改掉其中任何一个都会让网络上的大量页面崩溃,所以 TC39(维护这门语言的委员会)选择了另一条路:提供一种可选的模式,把这些锋利的边角磨平。

这种可选模式就叫严格模式。在文件或函数顶部写一行就能开启:

运行它,你会得到 ReferenceError: x is not defined。如果没有这条指令,同样的代码会愉快地创建一个名为 x 的全局变量,并打印 10。同一门语言,两套不同的规则。

'use strict' 指令

这条指令只是一个字符串字面量:'use strict'; 或 "use strict";。它必须是脚本或函数的第一条语句。只要它前面有任何东西,哪怕只是一个多余的表达式,引擎都会把它当作普通字符串而忽略掉。

你可以把指令放进某个函数内部,让严格模式只作用于这一个函数。实际上现在已经没人这么做了:要么写模块(默认就是严格模式),要么给整个文件开启。

严格模式到底改变了什么

严格模式不是一条规则,而是一组规则。要点如下:

  • 给未声明的变量赋值会抛出错误,而不是创建全局变量。
  • 普通函数调用中的 this 是 undefined,而不是全局对象。
  • 重复的参数名(function f(a, a) {})是语法错误。
  • 给只读属性赋值会抛出错误,而不是静默失败。
  • 删除普通变量或函数会抛出错误。
  • 几个为将来保留的关键字(implements、interface、package、private、protected、public、static、yield)不能用作变量名。

简单看一下:

每一条都是“引擎更早地注意到了一个可能的错误”。这就是严格模式的全部价值。

静默的全局变量 bug

这是严格模式存在的最大理由。没有它,一个拼写错误就会变成一个全局变量:

// Non-strict mode (don't do this)
function setup() {
    usernmae = 'Ada';   // typo - creates window.usernmae
}

setup();
console.log(username);  // undefined - the real variable was never set

程序照常运行,没有报错。这个 bug 要到后面才会暴露出来:某个本该读取 username 的地方拿到了 undefined。在严格模式下,同样的拼写错误一运行就会抛出错误,你几秒钟就能修好,而不是花上几个小时。

现代 JavaScript 默认就是严格模式

这一点常让人意外:你今天写的大多数 JavaScript 已经处于严格模式中,而你根本没在任何地方写过 'use strict'。

自动启用严格模式的两大来源:

  • ES 模块。 任何 .mjs 文件、任何 <script type="module">,以及任何使用 import/export 导入的代码,都运行在严格模式下。不需要指令。
  • 类体。 class { ... } 内部的每条语句都是严格模式,即使所在文件不是。

那什么时候才需要自己写 'use strict'; 呢?只有在经典脚本中:用普通 <script> 标签加载的旧式 JS,或者使用 require 且没有模块打包的较旧的 Node.js 文件。对于任何新代码,模块都会替你完成这件事。

严格模式改变了普通函数中的 this

有一个行为值得特别说明,因为它常坑到新手。在非严格模式下,不通过对象直接调用一个普通函数时,this 会绑定到全局对象(浏览器中是 window,Node 中是 global)。在严格模式下,它绑定到 undefined:

这通常正是你想要的。如果 this 是 undefined,忘记绑定方法的 bug 会立刻暴露出来。如果它悄悄指向全局对象,bug 就会一直藏着,直到后面某个地方出问题。

严格模式不做什么

严格模式是在编译期和运行时收紧规则,它不会给你类型,不会捕获空值访问,也不会提示未使用的变量。这些是 TypeScript、ESLint 和编辑器的工作。把严格模式看作修复一组特定的语言级陷阱,而不是一张完整的安全网。

它也不等于“现代 JavaScript”。你可以在严格模式下写出糟糕的代码,也可以不写这条指令就写出出色的代码。它只是让几种特定的错误更难犯。

要点回顾

  • 在文件或函数顶部写上 'use strict';,就会启用 JavaScript 更严格的变体。
  • 拼写错误不再创建全局变量,this 不再悄悄变成全局对象,其他一些陷阱也会变成报错。
  • ES 模块和 class 类体会自动启用严格模式,你很少需要手写这条指令。
  • 它的意义在于更早发现 bug,而不是启用新功能。

下一篇:注释

你已经见过指令、分号以及引擎在意的规则。接下来是你写给未来读者的说明:JavaScript 注释如何工作,以及什么时候值得写注释。

常见问题

JavaScript 中的严格模式是什么?

严格模式是 JavaScript 的一种可选变体,它把一些静默的错误变成真正的报错。在文件或函数顶部写上字符串 'use strict'; 就能启用它。它让语言更严格:给未声明的变量赋值会抛出错误,重复的参数名是非法的,普通函数调用中的 this 是 undefined 而不是全局对象。

怎样在 JavaScript 中启用严格模式?

把 'use strict'; 作为脚本或函数的第一条语句。它前面可以有注释和空行,但只要有任何其他语句,它就会变成一个没有任何作用的普通字符串。在现代代码中你很少需要自己写它:ES 模块(.mjs 文件或 <script type="module">)和任何 class 的类体都会自动启用严格模式。

为什么要在 JavaScript 中使用严格模式?

它能更早地发现 bug。在非严格模式下,像 usernmae = 'Ada' 这样的拼写错误会悄悄创建一个全局变量;在严格模式下,它会抛出 ReferenceError。严格模式还会保留未来的关键字,在较旧的引擎中禁止重复的属性名,并让 this 的行为可预测,这些都让代码更容易理解。

现代 JavaScript 还需要 'use strict' 吗?

通常不需要。ES 模块和 class 类体默认就是严格模式,而大多数现代项目要么是前者,要么是后者。只有在旧式的经典脚本中,也就是用普通 <script> 标签、不带 type="module" 加载的脚本中,你才需要显式写上 'use strict';。

Coddy 编程语言插图

和 Coddy 一起学编程

开始