JavaScript 在早期为了降低上手门槛,设计得过于宽容,很多“静默失败”或容易引发 bug 的写法在当时都被允许。随着语言规模的膨胀,这些历史设计缺陷越来越成为工程化的绊脚石。ES5 引入了严格模式(Strict Mode),允许开发者选择一种更规范、更安全的运行方式。严格模式并不是一门新语言,而是 JavaScript 的一个“安全模式”开关。
2.5.1 开启方式
严格模式的开启非常简单,只需要在作用域顶端加一行字符串 'use strict' 或 "use strict" 即可。它的作用范围取决于你把这行声明放在哪里。
全局开启
在脚本文件(或 <script> 标签)最顶端添加严格模式声明,整个脚本都在严格模式下运行。注意,多个 <script> 标签之间是独立的,在一个标签中开启不会影响另一个。
<script>
"use strict";
// 现在所有代码都在严格模式下执行
var x = 3.14;
</script>
函数级开启
在函数体内部最顶端添加声明,只让该函数内部进入严格模式。这在只有部分代码需要严格检查、或者你无法控制整个脚本时特别有用。
function doSomething() {
"use strict";
// 在这个函数内是严格模式
}
// 函数外部仍然是普通模式
模块与类自动严格
ES6 引入的 class 和模块系统(import / export)默认自动严格模式,你不需要显式添加 "use strict" 声明。Node.js 中,.mjs 文件或项目 package.json 中配置 "type": "module" 后,所有模块也自动严格。
一个常见陷阱"use strict" 必须写在作用域的最顶端,中间不能有其他实际语句(注释除外),否则会被忽略。
// 错误:前面有代码
var x = 1;
"use strict"; // 无效!会被当作普通字符串
2.5.2 严格模式的作用
严格模式的核心目标可以概括为三点:
- 消除 JavaScript 中一些不合理、不严谨的语法,减少怪异行为。将之前“默默失败”的错误变成显式抛出异常,让 bug 尽早暴露。
- 修复一些阻碍引擎优化的缺陷,提升代码执行效率。比如禁用了
with语句,消除了变量查找中的不确定性,让引擎优化变得更加安全。 - 为未来新版本的 JavaScript 铺路,预留关键字。禁止将一些可能在未来成为关键字的词用作变量名。
简单说,严格模式让 JavaScript 变得更“现代”,帮助开发者写出更健壮、更容易维护的代码。大多数现代工程都会开启严格模式,ESLint 等工具也强烈推荐启用。
2.5.3 主要约束规则
严格模式下的限制覆盖了变量访问、作用域、函数调用、对象操作等多个方面。以下是开发者最常遇到、也最重要的规则:
一、变量必须声明
在普通模式下,直接给未声明的变量赋值会自动创建一个全局变量(隐式全局提升),这是导致命名污染和难以排查 bug 的元凶之一。严格模式下,这种行为会直接抛出 ReferenceError。
"use strict";
x = 10; // ReferenceError: x is not defined
二、禁止删除变量和不可删除的属性
在普通模式下,delete 运算符用于变量或某些不可配置属性时会静默返回 false。严格模式下,直接抛出 SyntaxError 或 TypeError。
"use strict";
var y = 20;
delete y; // SyntaxError: 不能删除变量
var obj = {};
Object.defineProperty(obj, "prop", { value: 1, configurable: false });
delete obj.prop; // TypeError:不能删除不可配置属性
三、函数的参数名必须唯一
普通模式下,如果定义函数时使用了重复的参数名,后面的参数会覆盖前面的,不报错。严格模式下直接抛出语法错误。
function sum(a, a, c) { // 严格模式下 SyntaxError
"use strict";
return a + c;
}
四、禁止八进制数字字面量
普通模式下,以 0 开头的数字可能被解释为八进制(如 012 代表十进制的 10),这容易造成混淆。严格模式下,直接用 0 前缀(不带 o)的写法会报错,必须使用 0o 或 0O(ES6 标准写法)。
"use strict";
var num = 0755; // SyntaxError
var num = 0o755; // 正确,八进制写法
五、eval 和 arguments 限制
- 不能用作标识符:严格模式下,
eval和arguments不能作为变量名、函数名或参数名。 - eval 拥有自己的作用域:在普通模式下,
eval("var x = 1")会在当前作用域创建变量x;在严格模式下,eval中的变量只存在于eval内部,不会泄漏到外层作用域。 - arguments 不再追踪参数变化:在普通模式下,修改
arguments对象的值会反过来改变对应的参数值,反之亦然。严格模式下,arguments只是参数的一个副本,与参数相互独立。
"use strict";
function test(a) {
a = 2;
console.log(arguments[0]); // 仍然是 1,不受影响
}
test(1);
六、this 值不再自动包装为对象
普通模式下,如果一个函数的 this 是 null 或 undefined,会被自动替换为全局对象(浏览器中是 window)。严格模式下,this 保持原值,不进行任何转换。这让函数调用的 this 更可预测。
"use strict";
function showThis() {
console.log(this);
}
showThis(); // undefined(普通模式下是 window)
同样,在严格模式下通过 call、apply、bind 传入的原始值不会被装箱为对象。
七、禁止 with 语句
with 语句允许将某个对象添加到作用域链的头部,使代码可以直接访问该对象的属性,但会导致代码混乱、性能低下,并且引擎无法优化。严格模式下直接禁止使用 with,出现就报错。
"use strict";
with (Math) { // SyntaxError
var x = cos(3);
}
八、保留关键字增加
为将来可能的语言扩展,严格模式新增了一些保留关键字:implements、interface、let、package、private、protected、public、static、yield。这些词不能作为变量名或函数名。
2.5.4 使用建议
在实际开发中,你有两个选择:
- 全项目严格模式:所有脚本和函数顶部都加上
"use strict"。这是推荐做法,几乎所有现代框架和构建工具都默认启用严格模式。 - 函数级严格模式:当你的代码需要与某些不兼容严格模式的旧代码混用时,只对特定函数开启严格模式,是平稳迁移的实用策略。
另外,在使用 Babel 或 TypeScript 等工具时,它们会自动处理严格模式的转换,你甚至不需要手动添加声明,但理解其规则仍然非常重要——它将影响你调试时实际看到的行为。
以后在学习和编写代码时,建议默认以严格模式思考,把它当作 JavaScript 的“正常”运行方式,这样既能避免大多数经典坑点,也为未来的代码维护打下良好基础。