JavaScript 严格模式完全指南

小飞兽 JavaScript 346 次阅读 2026-05-29

JavaScript 严格模式完全指南

Introduction

JavaScript 严格模式(Strict Mode)是 ECMAScript 5 引入的一种特殊执行模式,通过在脚本或函数顶部添加 "use strict" 指令来启用。在严格模式下,JavaScript 引擎会对代码执行更严格的检查,消除一些语言中的静默错误(silent errors),让代码行为更加明确、可预测,同时为未来的 ECMAScript 特性做好准备。

严格模式并不是 JavaScript 的一个子集——它是对语法的扩展,在语义上做了重要改变。很多在非严格模式( sloppy mode)下被容忍的"问题代码"在严格模式下会直接抛出错误。掌握严格模式不仅能帮助我们写出更高质量的代码,还能更好地理解 JavaScript 语言的设计缺陷与演进方向。本篇文章将从基础语法出发,逐一讲解严格模式的行为变更、代码示例、运行效果以及常见问题。

基础语法

1. 如何启用严格模式

严格模式通过字符串字面量 "use strict"(或 'use strict')来启用。它必须出现在脚本或函数体的最顶部,任何语句之前。

// 整个脚本启用严格模式(脚本顶部)
"use strict";
const message = "Hello strict mode";
console.log(message);
// 函数级别严格模式
function strictFunction() {
    "use strict";
    // 函数内所有代码都处于严格模式
    return "I am strict!";
}

function normalFunction() {
// 这里不是严格模式
return "I am normal!";
}

// ES6 模块默认处于严格模式,无需声明
// module.js(自动严格模式)
export const name = "module";
export default function() {
    // 隐式严格模式
    imei = 123; // 直接报错!
};

2. 严格模式主要行为变化

    • 禁止隐式全局变量:未声明的变量赋值会抛出 ReferenceError,而非自动创建全局变量。
    • 禁止删除不可删除的属性:对 varfunction 等不可删除的属性执行 delete 会抛出 SyntaxError
    • 禁止函数参数名重复:命名参数在同一个函数中不能出现重复。
    • 禁止八进制字面量0123 这样的八进制数字语法在严格模式下是语法错误。
    • 禁止 with 语句:严格模式下不允许使用 with
    • eval 和 arguments 的特殊限制:不能对 evalarguments 进行赋值或操作。
    • 只读属性赋值报错:对只读属性赋值、对 getter-only 属性赋值都会抛出错误。
    • this 的行为变化:在非严格模式下,this 在函数调用中可能指向全局对象;严格模式下 thisundefined(除非被显式指定)。

    代码示例

    示例一:禁止隐式全局变量

    "use strict";
    

    // 未声明变量直接赋值
    try {
    undeclaredVar = "Hello"; // ReferenceError: undeclaredVar is not defined
    console.log(undeclaredVar);
    } catch (e) {
    console.error("错误:", e.message);
    }

    // 正确做法:先声明
    const declaredVar = "Hello, declared!";
    console.log(declaredVar); // Hello, declared!

    示例二:this 在严格模式下的行为

    "use strict";
    

    function showThis() {
    console.log(this);
    }

    showThis(); // undefined(在非严格模式下会输出全局对象 Window)

    // 构造函数
    function Person(name) {
    this.name = name;
    console.log(this); // Person { name: "Alice" }
    }

    const alice = new Person("Alice");

    // 显式指定 this
    const obj = { value: 42 };
    showThis.call(obj); // { value: 42 }
    showThis.apply(obj); // { value: 42 }
    showThis.bind(obj)(); // { value: 42 }

    示例三:禁止八进制字面量

    "use strict";
    

    try {
    eval("const num = 010; console.log(num);"); // 语法错误
    } catch (e) {
    console.error("八进制错误:", e.message);
    }

    // ES6 推荐写法
    const numHex = 0o10; // 八进制(显式写法)
    console.log(numHex); // 8

    const numBin = 0b1010; // 二进制
    console.log(numBin); // 10

    示例四:禁止删除不可删除属性

    "use strict";
    

    const obj = { a: 1 };
    Object.defineProperty(obj, "readOnly", {
    value: 100,
    writable: false,
    configurable: false
    });

    try {
    delete obj.readOnly; // TypeError: Cannot delete property
    } catch (e) {
    console.error("删除只读属性错误:", e.message);
    }

    // 可以删除的配置属性
    delete obj.a;
    console.log(obj); // {}(成功删除)

    示例五:禁止函数参数名重复

    "use strict";
    

    try {
    eval("function sum(a, a, c) { return a + a + c; }"); // 语法错误
    } catch (e) {
    console.error("参数重复错误:", e.message);
    }

    // 替代方案:使用剩余参数
    function sum(...args) {
    return args.reduce((acc, val) => acc + val, 0);
    }

    console.log(sum(1, 2, 3)); // 6

    示例六:arguments 对象的限制

    "use strict";
    

    function trackArgs(a) {
    console.log("初始:", arguments[0]);
    a = 999;
    console.log("修改后:", arguments[0]); // 在严格模式下与 a 不同步了
    console.log("a =", a);
    console.log("arguments[0] =", arguments[0]);
    }

    trackArgs(10);
    // 输出:
    // 初始: 10
    // 修改后: 10
    // a = 999
    // arguments[0] = 10

    示例七:eval 作用域隔离

    "use strict";
    

    const evalVar = "外部变量";

    try {
    eval("const evalVar = 123; console.log(evalVar);"); // 123
    } catch (e) {
    console.error("eval 错误:", e.message);
    }

    // eval 创建的变量不会泄漏到外部
    console.log(typeof evalVar); // "string"(外部变量不受影响)

    示例八:禁止 with 语句

    "use strict";
    

    try {
    eval("with (Math) { console.log(PI, sqrt(2)); }"); // SyntaxError
    } catch (e) {
    console.error("with 错误:", e.message);
    }

    // 替代写法
    console.log(Math.PI, Math.sqrt(2)); // 3.141592653589793 1.4142135623730951

    运行效果

    在浏览器控制台或 Node.js 环境运行上述所有示例代码,主要输出如下:

    // 示例一
    错误: undeclaredVar is not defined
    Hello, declared!
    

    // 示例二
    undefined
    Person { name: "Alice" }
    { value: 42 }

    // 示例三
    八进制错误: Unexpected eval or arguments in strict mode
    8
    10

    // 示例四
    删除只读属性错误: Cannot delete property
    {}

    // 示例五
    参数重复错误: Unexpected eval or arguments in strict mode
    6

    // 示例六
    初始: 10
    修改后: 10
    a = 999
    arguments[0] = 10

    // 示例七
    123
    string

    // 示例八
    with 错误: Strict mode code may not include with statements
    3.141592653589793 1.4142135623730951

    常见问题

    Q1:严格模式会影响 JavaScript 性能吗?

    从理论上讲,严格模式省略了一些动态检查(如隐式全局变量的创建),引擎可以进行更多优化,实际上严格模式往往性能更好。现代 JavaScript 引擎(如 V8)已经对严格模式做了充分优化,不必担心性能损失。反而是非严格模式需要维持向后兼容,引擎内部仍有大量历史包袱。

    Q2:严格模式和非严格模式可以混合使用吗?

    可以,但需要注意作用域边界。一个脚本文件可以同时包含两种模式的代码:全局脚本顶部启用严格模式,则整个文件都是严格模式;仅在函数顶部启用,则只有该函数内部是严格模式。不过在实践中,建议整个项目统一使用严格模式,避免行为不一致。

    // 文件顶部
    "use strict";
    

    // 这里都是严格模式

    function mixed() {
    // 这里也是严格模式
    "use strict";
    // 仅这里更严格(函数级别的严格模式)
    }

    Q3:严格模式会让现有代码报错吗?

    有可能。如果现有代码存在未声明变量赋值、参数名重复、使用八进制字面量等"问题写法",引入严格模式后会立即报错。建议在引入严格模式前先使用 ESLint 等工具扫描问题代码,逐步修复。

    // 非严格模式下合法,严格模式下报错的一些代码
    "use strict";
    

    // 报错 1:未声明变量
    // imei = 123; // ReferenceError

    // 报错 2:八进制
    // const port = 0755; // SyntaxError

    // 报错 3:参数名重复
    // function foo(a, b, a) {} // SyntaxError

    // 报错 4:delete 变量
    // const x = 1;
    // delete x; // SyntaxError

    Q4:严格模式和 ES6 模块有什么关系?

    ES6 模块(<script type="module"> 或使用 import/export 的文件)默认处于严格模式,无需显式声明 "use strict"。这意味着在 ES6 模块中书写的所有代码都遵循严格模式的规则。这也是为什么在使用 import 时要注意变量必须先声明再使用。

    // ES6 模块中,以下代码会报错(自动严格模式)
    // imei = 123; // ReferenceError
    export const version = "1.0.0";
    

    Q5:严格模式对 try...catch 有什么影响?

    严格模式下,catch 子句中的异常参数不再共享同一个作用域,且该参数不能在 catch 块外访问。这实际上让代码更加安全,避免了异常变量泄漏到外部作用域。

    "use strict";
    

    try {
    JSON.parse("{invalid}");
    } catch (e) {
    console.error("解析错误:", e.message);
    }

    // e 在此处不可访问
    try {
    console.log(e.message);
    } catch (e) {
    // 这里是新的作用域
    console.log("e 的作用域已结束");
    }

    Q6:严格模式下 arguments 和形参是分离的吗?

    在严格模式下,函数的 arguments 对象与形参之间不再保持同步。修改形参的值不会影响 arguments 数组,反之亦然。这与非严格模式不同(非严格模式下两者保持联动)。这使得函数行为更加可预测,减少了意外的副作用。

    延伸阅读

    • 《JavaScript 高级程序设计》(第4版) 第 3 章"基本概念",关于严格模式有详细说明。
    • 《你不知道的 JavaScript(上卷)》 第 1 章"作用域",深入分析了 JS 的语义与严格模式设计动机。
  • Babel 与严格模式:现代构建工具通常默认启用严格模式兼容处理,了解 Babel 如何处理严格模式转换。

严格模式是 JavaScript 从"草莽时代"走向"成熟语言"的重要一步。虽然它不是银弹,但通过强制消除语言中一些历史遗留的糟粕,让代码质量更高、更安全。建议在所有新项目中默认启用严格模式,并通过 ESLint 的 strict 规则确保代码合规。对于遗留代码,可以逐步迁移,在修复问题时顺势引入严格模式。