在JavaScript中使用严格模式(Strict Mode)

前言

ECMAScript5中引入的严格模式,通过让JavaScript运行环境对一些开发过程中最常见和不易发现的错误做出和当前不同的处理,来让开发者拥有一个”更好”的JavaScript语言。很长一段时间内,由于只有Firefox支持严格模式,我曾对严格模式表示怀疑。但到了今天,所有主流的浏览器都在他们的最新版本中支持了严格模式(包括IE10,Opera12和Android4,IOS5)是时候开始使用严格模式了。

严格模式能起到什么作用?

严格模式为JavaScript引入了很多变化,我把他们分为两类(明显的和细微的)。细微改进的目标是修复当前JavaScript中的一些细节问题,对于这些问题我不在这里进行深入介绍;我在这里主要介绍严格模式引入的明显变化,那些在你使用严格模式前应该知道的概念和那些对你帮助最大的改变。

在开始学习具体特性前,请记住严格模式的一大目标是让你能更快更方便的调试。运行环境在发现问题时显性的抛出错误比默不做声的失败或怪异行事(未开启严格模式的JavaScript运行环境经常这样)要好。严格模式会抛出更多错误,但这是好事,因为这些错误会唤起你注意并修复很多以前很难被发现的潜在问题。

1. 去除with关键词

首先,严格模式中去除了with语句,包含with语句的代码在严格模式中会抛出异常。所以使用严格模式的第一步:确保你的代码中没有使用with。

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

2. 防止意外为全局变量赋值

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

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

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

严格模式中另一个重要的变化是函数中未被定义或为空( null or 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属性。

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

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

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

5. 安全的 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);
}());

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

ECMAScript5中还引入为对象的特定属性设为只读,或让整个对象不可修改的能力。 但在非严格模式中,尝试修改一个只读属性只会默不做声的失败。 在你和一些浏览器原生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”就假定余下的代码能正常工作。最后,请在严格模式下开始编写更好的代码。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

时间: 2019-06-11

深入理解javascript严格模式(Strict Mode)

ECMAScript5中引入的严格模式,通过让JavaScript运行环境对一些开发过程中最常见和不易发现的错误做出和当前不同的处理,来让开发者拥有一个"更好"的JavaScript语言.很长一段时间内,由于只有Firefox支持严格模式,我曾对严格模式表示怀疑.但到了今天,所有主流的浏览器都在他们的最新版本中支持了严格模式(包括IE10,Opera12和Android4,IOS5)是时候开始使用严格模式了. 严格模式能起到什么作用? 严格模式为JavaScript引入了很多变化,我把

Javascript的严格模式strict mode详细介绍

"严格模式"(strict mode)是ECMA-262 Edition 5定义的新语法,表示要用严格的Javascript语法来执行,有一些过去惯用的写法就会抛出SyntaxError异常,例如:1.变量前没有用var宣告2.使用八进制语法:var n = 023和var s = "\047"3.使用with语句4.使用delete删除一个变量名(而不是属性名):delete myVariable5.使用eval或arguments作为变量名或函数名6.使用未来保

JavaScript中的console.profile()函数详细介绍

编写JavaScript程序时,如果需要知道某段代码的执行时间,可以使用console.time().不过,在分析逻辑较为复杂的JavaScript程序,试图从中找出性能瓶颈的时候,console.time()就不适用了 - 深入分析逻辑较为复杂的JavaScript程序的运行就意味着插入大量的console.time()语句,而这无疑是不可接受的.对于复杂逻辑的JavaScript程序调优,正确的方法是使用console.profile(). 浏览器支持 安装了Firebug插件的Firefo

JavaScript中的分号插入机制详细介绍

仅在}之前.一个或多个换行之后和程序输入的结尾被插入 也就是说你只能在一行.一个代码块和一段程序结束的地方省略分号. 也就是说你可以写如下代码 复制代码 代码如下: function square(x) {     var n = +x     return n * n } 但是却不可以写的像下面代码一样,这样就报错了哦 复制代码 代码如下: function area(r) {    r = +r    return Math.PI*r*r }//error 仅在随后的输入标记不能解析时插入

JavaScript中的console.time()函数详细介绍

如果需要在Web调试过程中知道代码执行的时间,那么可以通过在JavaScript代码中添加console.time()语句和console.timeEnd()语句来对程序的执行进行计时.以下面这个耗时较长的foo()函数为例: 复制代码 代码如下: function foo(){     var x = 4.237;     var y = 0;     for (var i=0; i<100000000; i++) {         y = y + x*x;     }     return

Javascript优化技巧之短路表达式详细介绍

什么是短路表达式? 短路表达式:作为"&&"和"||"操作符的操作数表达式,这些表达式在进行求值时,只要最终的结果已经可以确定是真或假,求值过程便告终止,这称之为短路求值.这是这两个操作符的一个重要属性. 一个最简单的例子: 复制代码 代码如下: foo = foo||bar; 这行代码是什么意思?答案: 复制代码 代码如下: //如果foo存在,值不变,否则把bar的值赋给foo if(!foo)     foo = bar; 在javascrip

JavaScript中的console.log()函数详细介绍

对于JavaScript程序的调试,相比于alert(),使用console.log()是一种更好的方式,原因在于:alert()函数会阻断JavaScript程序的执行,从而造成副作用:而console.log()仅在控制台中打印相关信息,因此不会造成类似的顾虑. 什么是console.log()? 除了一些很老版本的浏览器,现今大多数浏览器都自带调试功能:即使没有调试功能,也可以通过安装插件来进行补充.比如,老版本的Firefox没有自带调试工具,在这种情况下可以通过安装Firebug插件来

JavaScript中的console.group()函数详细介绍

在使用console.log()或者其它日志级别的控制台输出功能时,日志输出是没有层级关系的.当程序中日志输出较多时,这一局限性将带来不小的麻烦.为了解决这一问题,可以使用console.group().以下面代码为例: 复制代码 代码如下: function doTask(){     doSubTaskA(1000);     doSubTaskA(100000);     console.log("Task Stage 1 is completed");     doSubTas

javascript相等运算符与等同运算符详细介绍

==运算符和===运算符用来检测两个值是否相等,它们采用了具有同一特质的两个不同定义.这两个运算符都接受任意类型的运算数,如果两个运算数相等,它们都返回true,否则都返回false,===运算符是等同运算符,它采用严格的同一特质定义检测两个运算数是否完全相同,亲们注意是完全相同哦.==运算符是相等运算符,它采用比较宽松的同一特质定义比较两个运算数是否相等. 这样概念有个基本认识. 下面说下具体判定两个值是相等情况吧. ===运算符两个值比较 1,如果两个值类型不同,它们就不相同 2,如果两个值

javascript编码的几个方法详细介绍

escape定义和用法 escape() 函数可对字符串进行编码,这样就可以在所有的计算机上读取该字符串. 参数 描述 string 必需.要被转义或编码的字符串. 返回值 已编码的 string 的副本.其中某些字符被替换成了十六进制的转义序列. 说明 该方法不会对 ASCII 字母和数字进行编码,也不会对下面这些 ASCII 标点符号进行编码: * @ - _ + . / .其他所有的字符都会被转义序列替换. 提示和注释 提示:可以使用 unescape() 对 escape() 编码的字符

javascript基础之查找元素的详细介绍(访问节点)

当然这些选择器都是jQuery扩展的一些方法,那么使用原生js时要怎样来查找元素呢?今天就来简单梳理下. DOM定义了多种查找元素的方法,除了我们常用的getElementById(),还有getElementsByTagName()和getElementsByName().使用这几种方法方法我们可以查找html文档中的任意html元素.getElementById()首先来看下getElementById(),这个方法很简单,只需在参数中传入html标签的id属性值即可,由于html页面中的i