JavaScript的严格模式(strict mode)是ECMAScript 5引入的一项特性。如果你在脚本或函数的顶部声明 'use strict';
,你就启用了严格模式:
'use strict';
当JavaScript引擎看到这个指令时,它将开始以一种特殊的模式解释代码。在这种模式下,当检测到某些可能导致潜在错误的编码实践时,会抛出错误。这就是严格模式背后的基本原理。
严格模式的例子
考虑下面这个例子:
var a = 365;
var b = 030;
开发者可能因为强迫症而对齐数值字面量,但这样会无意中使变量 b
初始化为八进制字面量。在非严格模式下,这将被解释为值 24
(十进制)。然而,在严格模式下,这样的代码会抛出错误。
严格模式不仅限于八进制字面量的检测。它还包括许多其他特性,这里仅列举了几个例子。详细列表可以参见这篇回答:stack overflow链接。
在哪里使用 'use strict';
?
在新项目中
**绝对要用!**严格模式可以作为哨兵,当你在代码中做了愚蠢的事情时,它会报警。
在现有代码中
**可能不适合!**如果你的现有JavaScript代码包含严格模式中禁止的语句,应用程序会直接崩溃。如果你想使用严格模式,你需要准备好调试和修正现有代码。这就是为什么仅仅添加 'use strict';
并不能让你的代码立刻变得更好的原因。
如何使用严格模式?
在脚本顶部插入 'use strict';
:
// File: myscript.js
'use strict';
var a = 2;
...
这样,整个 myscript.js
文件中的代码都会以严格模式解释。
在函数体顶部插入 'use strict';
:
function doSomething() {
'use strict';
...
}
这样,函数 doSomething
的词法范围内的一切代码都会以严格模式解释。词法范围(lexical scope)在这里非常重要。例如,如果你的严格模式代码调用了一个非严格模式的库函数,只有你的代码在严格模式下执行,而被调函数则不会受影响。详情请参阅:这个回答。
严格模式下禁止的行为
作用域
历史上,JavaScript在函数的作用域上一直很迷惑。有时它们看起来是静态作用域,有些特性又让它们表现得像动态作用域。这使得程序难以阅读和理解,误解会导致bug,还会影响性能。严格模式要求所有变量绑定在编译时静态完成,因此一些需要动态作用域的特性要么被删除,要么被修改。具体来说,with
语句被删除,eval
函数篡改调用者环境的能力被严重限制。
隐式全局变量
JavaScript有隐式全局变量。如果你没有显式声明一个变量,JavaScript会为你隐式地创建一个全局变量。这对初学者很方便,但管理大型程序时会变得困难,并严重降低可靠性。在严格模式下,不再创建隐式全局变量,你需要显式声明所有变量。
全局泄漏
在某些情况下,this
会绑定到全局对象。例如,如果你忘记在调用构造函数时加上 new
前缀,构造函数的 this
会意外地绑定到全局对象,而不是初始化一个新对象。严格模式下,this
会绑定到 undefined
,从而抛出异常,使得错误能更快地被检测到。
无声失败
在JavaScript中,读写只读属性时会默默失败,程序会继续执行但属性值不会改变。这可能导致程序进入不一致状态。在严格模式下,尝试修改只读属性会抛出异常。
八进制
随着从机器码到高级语言的过渡,八进制表示法在编程语言中保留了下来,但在现代编程中几乎没有用处。更不幸的是,这个历史遗留问题被现代语言,包括JavaScript,全盘继承了。在严格模式下,八进制表示法不再被允许。
其他
arguments
伪数组在严格模式下变得更加类数组,失去了callee
和caller
属性。- 函数字面量中的重复键会产生语法错误。
- 函数不能有同名参数。
- 函数不能删除自己的变量。
- 尝试删除不可配置的属性会抛出异常。
- 原始值不再隐式包装。
未来JavaScript版本的保留字
ECMAScript 5添加了一些保留字。如果你在严格模式下使用这些保留字作为变量或参数名,会抛出错误。这些保留字包括:
implements
interface
let
package
private
protected
public
static
yield
延展阅读
通过了解和使用严格模式,你可以提高JavaScript代码的健壮性和可维护性,避免很多难以发现的bugs。建议在新项目中开始使用严格模式,但在切换现有项目时要小心,以免引发兼容性问题。