strict mode 与历史怪癖
基于现代 JavaScript(ES2025 · 核于 2026-06)
速查
- 开启:脚本 / 函数体首行
"use strict";;ES module 与class体默认严格,无需声明 - 严格模式把一批「悄悄失败」变「立即报错」:给未声明变量赋值 →
ReferenceError - 给只读 / getter-only / 不可扩展对象的属性赋值 →
TypeError(非严格下静默失败) - 删除不可删除属性 →
TypeError;函数重复形参名 →SyntaxError - 旧式八进制
0644被禁,须写0o644;with语句被禁 this在普通函数调用里是undefined(非严格下是全局对象 /window)eval有独立作用域,不再向外泄漏变量;arguments不再与具名参数联动implements/interface/let/package/private/protected/public/static/yield保留,不能作标识符- 必背怪癖:
typeof null === "object"、NaN !== NaN、ASI(自动分号插入)可能改变语义 - ASI 雷区:
return后换行会被插分号致返回undefined;行首(/[/`易与上一行连成一句
如何开启严格模式
显式声明 "use strict"
放在脚本最顶部(作用于整个脚本)或函数体首行(仅作用于该函数):
"use strict"; // 整个脚本严格
const v = 1;function strictFn() {
"use strict"; // 仅此函数严格
// ...
}注意:带剩余 / 默认 / 解构参数的函数不能再写函数级 "use strict"(语法错误),此时应在模块或外层开启。
module 与 class 默认严格
现代代码大多自动处于严格模式,无需手写指令:
- ES module(
<script type="module">或.mjs/ 打包产物):整个模块默认严格; class体:类声明与类表达式内部的所有代码默认严格。
// 一个 ES module 文件里
undeclared = 1; // 直接 ReferenceError —— 因为 module 默认严格
class C {
m() {
undeclared = 1; // 同样 ReferenceError —— class 体默认严格
}
}这正是「现代 JavaScript 天然更安全」的原因:你写 module / class 时,严格模式已经替你兜底。
严格模式收紧了什么
1. 禁止隐式全局变量
给未声明的变量赋值,非严格模式会悄悄创建全局变量(事故源头),严格模式直接报错:
"use strict";
mistyped = 17; // ❌ ReferenceError: mistyped is not defined2. 静默的属性赋值失败变成抛错
非严格模式下,给只读属性、getter-only 属性、不可扩展对象的新属性赋值都静默失败;严格模式抛 TypeError,让 Bug 暴露:
"use strict";
const obj = {};
Object.defineProperty(obj, "x", { value: 1, writable: false });
obj.x = 2; // ❌ TypeError(非严格:静默失败,x 仍是 1)
undefined = 5; // ❌ TypeError
NaN = 5; // ❌ TypeError(这些全局只读值不可写)
const frozen = Object.freeze({ a: 1 });
frozen.a = 9; // ❌ TypeError(非严格:静默失败)3. 删除不可删除属性抛错
"use strict";
delete Object.prototype; // ❌ TypeError
delete window.someGlobal; // 视情况 TypeError4. 重复形参名是语法错误
function sum(a, a, c) {
// ❌ SyntaxError(严格模式)
"use strict";
return a + a + c;
}5. 禁旧式八进制、禁 with
"use strict";
const n = 0644; // ❌ SyntaxError,须写 0o644
const m = 0o644; // ✅ 现代八进制写法
with (obj) {
// ❌ SyntaxError:with 在严格模式被禁
}6. this 在普通调用里是 undefined
非严格模式下,普通调用(非方法、非 new)的函数里 this 指向全局对象(浏览器是 window);严格模式下是 undefined——避免意外污染全局:
"use strict";
function fn() {
return this;
}
console.log(fn()); // undefined(非严格:window / globalThis)
console.log(fn.call(42)); // 42(显式指定的 this 不会被包装成对象)7. eval 与 arguments 行为收紧
"use strict";
eval("var leaked = 1;");
console.log(typeof leaked); // "undefined"(eval 不再向外泄漏变量)
function f(a) {
"use strict";
a = 99;
return arguments[0]; // 严格模式返回原始实参,不随 a 变(非严格会变 99)
}
console.log(f(1)); // 18. 额外保留字
严格模式额外保留这些字,不能用作变量 / 函数名(为将来语言特性预留):implements、interface、let、package、private、protected、public、static、yield。
"use strict";
let public = 1; // ❌ SyntaxError: Unexpected strict mode reserved word必背历史怪癖
即便在严格模式,下面这些语言层面的历史包袱依旧存在,必须记住。
typeof null === "object"
JavaScript 第一版的实现遗留,因兼容性无法修复。判断 null 用 === null:
typeof null; // "object" ← 不是 "null"
const v = null;
v === null; // true ← 正确判断方式NaN 不等于自身
NaN 是唯一 x !== x 成立的值,检测要用 Number.isNaN(详见 转换与相等页):
NaN === NaN; // false
Number.isNaN(NaN); // true ← 正确检测自动分号插入(ASI)
JavaScript 允许省略行尾分号,引擎会在解析出错时「自动补分号」。绝大多数时候它如你所愿,但少数情况会改变语义,这是不写分号风格最大的风险点。
ASI 三大雷区
雷区 1:return 后换行。 引擎会在 return 后补分号,导致返回 undefined:
function bad() {
return; // ← ASI 在此补了分号!
{
ok: 1;
} // 这段永远不会被返回
}
console.log(bad()); // undefined
function good() {
return {
// ← 左花括号紧跟 return 同一行才对
ok: 1,
};
}throw、break、continue、++ / --(后缀)后换行也有类似问题。
雷区 2:下一行以 ( 开头。 会被当作上一行结果的「函数调用」:
const a = 1
const b = a
(function () {})() // ← 实为 a(function(){})(),TypeError: a is not a function雷区 3:下一行以 [ 或 ` 开头。 [ 被当成属性访问 / 索引,` 被当成标签模板:
const x = obj
[key].doSomething() // ← 实为 obj[key],可能不是你想要的ASI 的实用结论
两种风格都能写出正确代码,但要二选一并贯彻:
- 全程写分号(多数团队 / Prettier 默认)——彻底规避 ASI 歧义;
- 无分号风格——则必须给以
(/[/`开头的行前置分号(;(function(){})()),并牢记return不能换行。
无论哪种,都交给 Prettier / ESLint 自动处理,别靠手动记忆。
小结
严格模式(module / class 默认开启)把隐式全局、静默赋值失败、this 指向全局等一批坑变成显式报错,是现代代码的安全基线。同时记牢三个绕不开的历史怪癖:typeof null === "object"、NaN !== NaN、以及 ASI 在 return 换行等场景下会改变语义。至此本叶的语言基础全部讲完,更系统的速查与权威链接见 参考。