深入理解JavaScript严格模式:特性、用法与最佳实践

作者:木棉时间:2026-10-28 16:51:50浏览:373

本文深入解析JavaScript严格模式(Strict Mode)的核心机制与实用价值。从ECMAScript 5引入的背景出发,详细阐述了移除with语句、禁止未声明变量赋值、修正this指向、防止重名参数及只读属性修改等关键变化。文章提供了在函数作用域内安全启用严格模式的代码示例,并强调避免全局启用以保护第三方代码,旨在帮助开发者通过更严格的语法检查提升代码质量与可维护性。

严格模式的引入与普及

ECMAScript 5引入了严格模式,旨在通过让JavaScript运行环境对开发中常见且隐蔽的错误采取不同的处理策略,从而为开发者提供更优质的语言体验。过去很长一段时间,由于仅有Firefox支持该特性,我对严格模式的普及持怀疑态度。然而如今,所有主流浏览器(包括IE10、Opera 12以及Android 4和iOS 5)的最新版本均已支持严格模式,现在正是全面采用严格模式的时机。

严格模式究竟能带来哪些实际作用?

严格模式为JavaScript引入了诸多变更,可归纳为显著变化与细微改进两类。细微改进旨在修复现有JavaScript中的一些细节问题,此处不作深入展开;若感兴趣,推荐阅读Dmitry Soshnikov撰写的精彩文档《ECMA-262-5 in Detail Chapter 2 Strict Mode》。本文重点介绍严格模式带来的显著变化,即那些在使用前必须知晓且对开发帮助最大的核心概念。

在深入具体特性之前,需牢记严格模式的一大核心目标是加速并简化调试过程。运行环境在发现问题时主动抛出错误,远胜于默不作声地失败或表现出怪异行为(这是未开启严格模式时的常见现象)。虽然严格模式会抛出更多错误,但这无疑是好事,因为这些错误能促使你注意并修复许多以往难以察觉的潜在问题。

严格模式的核心特性

移除with语句

首先,严格模式彻底移除了with语句,包含with的代码在严格模式下将直接抛出异常。因此,启用严格模式的第一步是确保代码中不存在任何with语句。

代码如下:

// 在严格模式中以下JavaScript代码会抛出错误
with (location) {
alert(href);
}

防止意外为全局变量赋值

其次,在严格模式下,局部变量在赋值前必须先进行声明。在启用严格模式之前,为一个未声明的局部变量赋值时,会自动创建一个同名全局变量。这是JavaScript程序中最容易出现的错误之一,而在严格模式下尝试这样做时,会抛出显性的异常。

代码如下:

// 严格模式下会抛出异常
(function() {
someUndeclaredVar = "foo";
}());

函数中的this不再默认指向全局

严格模式下的另一个重要变化是,函数中未被定义或为空(null或undefined)的this不再默认指向全局环境(global)。这会导致一些依赖函数中默认this行为的代码执行出错,例如:

代码如下:

window.color = "red";
function sayColor() {
alert(this.color);
}
// 在strict模式中会报错, 如果不在严格模式中则提示 “red"
sayColor();
// 在strict模式中会报错, 如果不在严格模式中则提示 “red"
sayColor.call(null);

this在被赋值之前会一直保持为undefined,这意味着当一个构造函数在执行时,如果之前没有明确的new关键词,会抛出异常。

代码如下:

function Person(name) {
this.name = name;
}
//在严格模式中会报错
var me = Person("Nicholas");

在上面的代码中,Person构造函数运行时因为之前没有new,函数中的this会保留为undefined。由于你不能为undefined设置属性,上面的代码会抛出错误。在非strict模式环境中,没有被复制的this会默认指向window全局变量,运行的结果将是意外地为window全局变量设置name属性。

防止重名

当编写大量代码时,对象属性和函数参数很容易一不小心被设置成一个重复的名字。严格模式在这种情况下会显性地抛出错误。

代码如下:

//重复的变量名,在严格模式下会报错
function doSomething(value1, value2, value1) {
//code
}
//重复的对象属性名,在严格模式下会报错:
var object = {
foo: "bar",
foo: "baz"
};

以上的代码在严格模式中都会被认为是语法错误而在执行前就让你能得到提示。

安全的EVAL()

虽然eval()语句最终没有被移除,但在严格模式中仍然对它进行了一些改进。最大的改变是在eval()中执行的变量和函数声明不会直接在当前作用域中创建相应的变量或函数,例如:

代码如下:

(function() {
eval("var x = 10;");
// 非严格模式中,alert 10
// 严格模式中则因x未被定义而抛出异常,
alert(x);
}());

任何在eval()执行过程中创建的变量或者函数保留在eval()中。但你能明确地从eval()语句的返回值来获取eval()中的执行结果,例如:

代码如下:

(function() {
var result = eval("var x = 10, y = 20; x + y");
// 在strict或非strict模式中都能正确的运行余下的语句.(resulst为30)
alert(result);
}());

对只读属性修改时抛出异常

ECMAScript 5中还引入了将对象的特定属性设为只读,或让整个对象不可修改的能力。但在非严格模式中,尝试修改一个只读属性只会默不作声地失败。在你和一些浏览器原生API打交道过程中,你很可能遇到这种情况。严格模式会在这种情况下明确地抛出异常,提醒你修改这个属性是不被允许的。

代码如下:

var person = {};
Object.defineProperty(person, "name" {
writable: false,
value: "Nicholas"
});
// 在非严格模式时,沉默的失败,在严格模式则抛出异常.
person.name = "John";

上面的例子中,name属性被设为只读,非严格模式中执行对name属性的修改不会引发报错,但修改不会成功。但严格模式则会明确地抛出异常。

NOTE: 强烈建议你在使用任何ECMAScript属性特性指定时开启严格模式。

严格模式的正确启用方式

如何使用?

在现代浏览器中开启严格模式非常容易,只需要在JavaScript代码中出现以下指令即可

"use strict";

虽然看上去上面的代码仅仅只是未赋予某个变量的字符串,它实际上起到指示JavaScript引擎切换到严格模式的作用(不支持严格模式的浏览器会忽略以上代码,不会对后续的执行产生任何影响)。虽然你能把这个指令作用到全局或某个函数中,但这里还是要提醒,不要在全局环境下启用严格模式。

代码如下:

// 请不要这么使用
"use strict";
function doSomething() {
// 这部分代码会运行于严格模式
}
function doSomethingElse() {
// 这部分代码也会运行于严格模式
}

 

虽然上面的代码看起来不算一个大问题。但当你不负责维护页面中引入的全部代码时,这样使用strict模式会让你面临由于第三方代码没有为严格模式做好准备而引发的问题。

因此,最好把开启严格模式的指令作用于函数中,例如:

代码如下:

function doSomething() {
"use strict";
// 这个函数中的代码将会运行于严格模式
}
function doSomethingElse() {
// 这个函数中代码不会运行于严格模式
}

 

如果你想让严格模式在不止一个函数中开启,请使用立即执行函数表达式(immediately-invoked function expression, IIFE):

代码如下:

(function() {
"use strict";
function doSomething() {
// 这个函数运行于严格模式
    }
function doSomethingElse() {
// 这个函数同样运行于严格模式
    }
}());

总结与建议

结论

我强烈建议你从现在开始就启用JavaScript严格模式,它能帮你发现代码中未曾注意到的错误。不要在全局环境中启用,但你能尽量多地使用IIFE(立即执行函数表达式)来把严格模式作用到多个函数范围内。一开始,你会遇到之前未曾碰到过的错误提示,这是正常的。当启用严格模式后,请确保在支持的浏览器中做了测试,以发现新的潜在问题。一定不要仅仅在代码中添加一行“use strict”就假定余下的代码能正常工作。最后,请在严格模式下开始编写更好的代码。

注: 这里有各款浏览器对严格模式支持情况的一个汇总。 可以在这个页面对当前浏览器的严格模式支持度进行测试。

严格模式的优势:

使JavaScript更牢固 1. This不再被封装,在normal mode下,this一直是对象。 2. Fun.caller和fun.arguments即不是可以删除的属性,也不能被set或retrieved。 3. Arguments.caller也是不可以删除的属性,也不能set或retrieved。

为将来的ECMAScript版本铺平道路 1. 增加了下列保留字:implements, interface, let,package, private, protected, public, static和yield 。 2. 方法声明应该放在脚本或方法的最前面,不能放在if或for等语句中间。

作者最新文章