严格模式:编写更安全的 JavaScript
在航空领域,飞行员必须遵守严格的规程和检查清单,即使看似繁琐,但这些规则能防止灾难性错误。JavaScript 的严格模式(strict mode)就是类似的概念——它为语言添加了更严格的规则和检查,帮助你及早发现潜在问题,写出更安全、更可靠的代码。
什么是严格模式
严格模式是 ES5(2009 年)引入的一种 JavaScript 执行模式。它改变了一些静默错误的行为,让它们抛出明显的错误;禁止了某些容易出问题的语法;并优化了某些操作的性能。
在严格模式下,JavaScript 会:
- 将一些静默失败的操作改为抛出错误
- 禁用一些容易混淆或不安全的特性
- 为未来的 ECMAScript 版本保留某些关键字
- 提供更好的错误检查和调试信息
如何启用严格模式
启用严格模式非常简单,只需在代码顶部添加一个字符串字面量:
"use strict";这个指令可以在两个层级使用:
1. 全局严格模式
在脚本文件的最开始使用,整个文件都会以严格模式运行:
"use strict";
// 整个文件都是严格模式
let x = 10;
console.log(x);2. 函数级严格模式
在函数体的开头使用,只有该函数以严格模式运行:
function strictFunction() {
"use strict";
// 这个函数是严格模式
let y = 20;
console.log(y);
}
function normalFunction() {
// 这个函数不是严格模式
z = 30; // 在非严格模式下,这会创建全局变量
}注意:"use strict" 必须出现在脚本或函数的最开始,前面只能有注释和空白符。如果放在其他位置,会被忽略:
// ❌ 不会生效
let x = 10;
("use strict"); // 太晚了,会被当作普通字符串
// ✅ 正确
("use strict");
let y = 20;ES6 模块自动使用严格模式
如果你使用 ES6 模块(import/export),整个模块会自动处于严格模式,不需要显式声明:
// module.js
// 自动处于严格模式,不需要 "use strict"
export function hello() {
console.log("Hello");
}ES6 类也自动运行在严格模式下:
class MyClass {
constructor() {
// 自动处于严格模式
this.value = 10;
}
}严格模式的主要限制
1. 禁止隐式全局变量
在非严格模式下,给未声明的变量赋值会自动创建全局变量。这是常见的 bug 来源:
// 非严格模式
function createUser() {
userName = "Alice"; // 创建了全局变量 userName
}
createUser();
console.log(userName); // "Alice"(全局变量被意外创建)在严格模式下,这会抛出错误:
"use strict";
function createUser() {
userName = "Alice"; // ReferenceError: userName is not defined
}
createUser();这迫使你明确声明变量:
"use strict";
function createUser() {
let userName = "Alice"; // ✅ 正确
return userName;
}2. 禁止删除变量、函数和参数
在严格模式下,不能使用 delete 删除变量、函数或函数参数:
"use strict";
let x = 10;
delete x; // SyntaxError: Delete of an unqualified identifier in strict mode
function test() {}
delete test; // SyntaxError
function doSomething(param) {
delete param; // SyntaxError
}可以删除对象的属性,但不能删除不可配置的属性:
"use strict";
let obj = { name: "Alice" };
delete obj.name; // ✅ 可以删除对象属性
delete Object.prototype; // TypeError: Cannot delete property 'prototype' of function Object()3. 禁止重复的参数名
// 非严格模式:后面的参数覆盖前面的
function sum(a, a, c) {
return a + a + c; // 只用第二个 a
}
console.log(sum(1, 2, 3)); // 2 + 2 + 3 = 7
// 严格模式:语法错误
("use strict");
function sum(a, a, c) {
// SyntaxError: Duplicate parameter name not allowed
return a + a + c;
}4. 禁止八进制字面量
// 非严格模式:八进制表示
let num1 = 010; // 8(十进制)
let num2 = 0888; // 888(因为包含 8,所以当作十进制)
// 严格模式:语法错误
("use strict");
let num = 010; // SyntaxError: Octal literals are not allowed in strict mode
// ✅ 使用现代的八进制语法
let num = 0o10; // 8(ES6 的八进制语法)5. 禁止对只读属性赋值
"use strict";
// 给只读属性赋值
let obj = {};
Object.defineProperty(obj, "x", { value: 42, writable: false });
obj.x = 100; // TypeError: Cannot assign to read only property 'x'
// 给不可扩展对象添加属性
let nonExtensible = { name: "Alice" };
Object.preventExtensions(nonExtensible);
nonExtensible.age = 30; // TypeError: Cannot add property age, object is not extensible
// 删除不可配置的属性
delete Object.prototype; // TypeError: Cannot delete property 'prototype'在非严格模式下,这些操作会静默失败,让 bug 难以发现。
6. eval 的作用域隔离
在严格模式下,eval 中声明的变量不会污染外部作用域:
// 非严格模式
eval("var x = 10");
console.log(x); // 10(eval 创建了外部变量)
// 严格模式
("use strict");
eval("var y = 20");
console.log(y); // ReferenceError: y is not defined这使得 eval 更安全(虽然仍然应该避免使用 eval)。
7. this 的行为变化
在非严格模式下,函数中的 this 如果是 undefined 或 null,会自动指向全局对象(浏览器中是 window):
// 非严格模式
function showThis() {
console.log(this); // window(浏览器环境)
}
showThis();在严格模式下,this 保持原值:
"use strict";
function showThis() {
console.log(this); // undefined
}
showThis();
// 对象方法中的 this 仍然正常工作
let obj = {
method: function () {
console.log(this); // obj
},
};
obj.method();这能防止意外修改全局对象:
// 非严格模式:危险!
function dangerous() {
this.value = 100; // 创建了全局变量 window.value
}
dangerous();
// 严格模式:安全
("use strict");
function safe() {
this.value = 100; // TypeError: Cannot set property 'value' of undefined
}
safe();8. 保留字
严格模式为未来的 ECMAScript 版本保留了一些关键字,不能用作变量名:
"use strict";
let implements = 10; // SyntaxError
let interface = 20; // SyntaxError
let let = 30; // SyntaxError
let private = 40; // SyntaxError
let protected = 50; // SyntaxError
let public = 60; // SyntaxError
let static = 70; // SyntaxError
let yield = 80; // SyntaxError9. 禁止 with 语句
// 非严格模式:with 语句(不推荐)
let obj = { a: 1, b: 2 };
with (obj) {
console.log(a); // 1
console.log(b); // 2
}
// 严格模式:语法错误
("use strict");
with (obj) {
// SyntaxError: Strict mode code may not include a with statement
console.log(a);
}with 语句会让代码难以理解和优化,应该避免使用。
实际应用场景
1. 及早发现错误
"use strict";
function calculateTotal(price, quantity) {
// ❌ 拼写错误会立即被发现
totla = price * quantity; // ReferenceError: totla is not defined
return total; // 这行不会执行
}
// 如果没有严格模式,totla 会成为全局变量,bug 难以发现2. 防止意外的全局变量
"use strict";
function processData(data) {
// ❌ 忘记声明变量
result = data.map((x) => x * 2); // ReferenceError
return result;
}
// ✅ 正确
function processData(data) {
let result = data.map((x) => x * 2);
return result;
}3. 更安全的 this 绑定
"use strict";
class User {
constructor(name) {
this.name = name;
}
greet() {
console.log(`Hello, ${this.name}`);
}
}
let user = new User("Alice");
let greetFn = user.greet;
// 非严格模式:this 是 window,this.name 是 undefined
// 严格模式:this 是 undefined,会报错
greetFn(); // TypeError: Cannot read property 'name' of undefined
// ✅ 正确的做法:绑定 this
let boundGreet = user.greet.bind(user);
boundGreet(); // Hello, Alice4. 配合工具和框架
现代的 JavaScript 工具和框架通常假设使用严格模式:
// React 组件(自动使用严格模式)
import React from "react";
function MyComponent() {
// 这里是严格模式
return <div>Hello</div>;
}
// ES6 模块(自动使用严格模式)
export function myFunction() {
// 这里是严格模式
}全局严格模式 vs 函数严格模式
全局严格模式的风险
在整个文件使用严格模式时要小心,特别是当你的代码会与其他脚本合并时:
// file1.js
"use strict";
// 整个文件是严格模式
// file2.js(第三方库,非严格模式)
// 可能依赖非严格模式的特性
// 如果两个文件被合并,file2.js 也会受严格模式影响,可能导致错误推荐的做法
- 使用 ES6 模块:自动严格模式,作用域隔离
- 使用 IIFE 包装:为单个文件创建独立作用域
- 或使用函数级严格模式:只在需要的函数中启用
// 方案 1:ES6 模块(推荐)
// module.js
export function myFunction() {
// 自动严格模式
}
// 方案 2:IIFE
(function () {
"use strict";
// 这个IIFE内部是严格模式
})();
// 方案 3:函数级
function strictFunction() {
"use strict";
// 只有这个函数是严格模式
}性能影响
严格模式在某些情况下能提升性能,因为它:
- 禁用了一些难以优化的特性(如
with、arguments.callee) - 让 JavaScript 引擎能做出更多假设和优化
- 简化了变量查找(不需要检查全局对象)
但这种性能提升通常很小,不应该是使用严格模式的主要原因。主要好处是更好的错误检查和代码质量。
常见陷阱
1. 忘记添加"use strict"
// ❌ 写在了注释后面
// This is my script
"use strict"; // ✅ 但这里仍然有效,因为注释不算
// ❌ 写在了代码后面
let x = 10;
("use strict"); // ❌ 不会生效2. 混合严格和非严格代码
function mixed() {
// 非严格模式
x = 10; // 创建全局变量
function inner() {
"use strict";
y = 20; // ReferenceError
}
inner();
}最好整个项目统一使用严格模式(通过 ES6 模块)。
3. arguments 对象的变化
function test(a) {
"use strict";
a = 42;
console.log(arguments[0]); // 在严格模式下不随 a 变化
}
test(10); // 输出: 10(非严格模式下会是 42)总结
严格模式是 JavaScript 的"安全带",虽然它增加了一些限制,但这些限制能帮助你写出更安全、更可靠的代码。在现代 JavaScript 开发中,由于 ES6 模块和类自动使用严格模式,你可能无需显式声明就已经在使用它了。
关键要点:
- 使用
"use strict"启用严格模式 - 严格模式禁止隐式全局变量
- 禁止重复参数、八进制字面量等容易出错的语法
this不再自动指向全局对象- ES6 模块和类自动使用严格模式
- 推荐在所有新项目中使用严格模式
- 使用现代构建工具和 ES6 模块可以自动获得严格模式的好处
严格模式不是限制你的自由,而是帮助你避免常见的陷阱。就像安全带不会限制你开车,但会在危险时刻保护你一样。养成使用严格模式的习惯,是成为专业 JavaScript 开发者的重要一步。