ECMAScript5将严格模式(strict mode)引入了Javascript中,目的是容许开发人员可以选择“更好”的Javascript版本,这个版本能用不一样的方式处理那些广泛而又臭名昭著的错误。一开始的时候,我对该模式抱着怀疑的态度,由于当时在只有一款浏览器(Firefox)支持严格模式。时至今日,全部的主流浏览器的最新版本——包括IE10与Opera12——都支持严格模式。使用严格模式的时机已经成熟了。
严格模式给Javascript的运行方式带来了许多不一样,我将它们分为了两类:明显的(obvious),以及微妙的(subtle)。那些微妙的改变是为了解决微妙的问题,我不打算在这里对其进行赘述。若是你对这些细节感兴趣,请参考Dmitry Soshnikov的精彩文章,《ECMA-262-5 in Detail. Chapter 2. Strict Mode》。我对介绍明显的变化更感兴趣:它们是你开始使用严格模式以前所必须了解的,也可能给你带来最多好处。 express
在开始介绍特殊特性以前,你须要记住,严格模式的目标之一是容许更快地调试错误。帮助开发者调试的最佳途径是当肯定的问题发生时抛出相应的错误(throw errors when certain patterns occur),而不是悄无声息地失败或者表现出奇怪的行为(这正是现在不在严格模式下的Javascript作的)。严格模式下的代码抛出更多的错误信息,这是好事,由于它能帮助开发者很快注意到一些必须当即解决的问题。 浏览器
首先,严格模式去除了with语句。当with语句出如今严格模式中时,它会被认为是非法的Javascript语句并抛出语法错误。因此,使用严格模式的第一步就是确保你没有在使用with。 安全
// 在严格模式中将致使语法错误 with (location) { alert(href); }
第二点是,变量在赋值以前必须声明。在非严格模式下,给一个未声明的变量赋值将自动生成一个该名字的全局变量。这是Javascript中最广泛的错误之一。严格模式中,这样作将抛出一个错误。 ecmascript
// 严格模式中抛出一个错误 (function() { someUndeclaredVar = "foo"; }());
另外一个重要的变化是,当this值为null或undefined时,不会再将其强制转换为全局对象。也就是说,this保留了它的原始值,也所以可能会致使一些依赖于强制转换的代码发生错误。例如: ide
window.color = "red"; function sayColor() { // 严格模式下,this不会指向window alert(this.color); } // 如下两种状况,在严格模式下都抛出错误 sayColor(); sayColor.call(null);
根本而言,this值必须赋值,不然将保留undefined值。这意味着调用构造函数时若漏掉了new关键字也会致使错误: 函数
function Person(name) { this.name = name; } // 严格模式下致使错误 var me = Person("Nicholas");
在这段代码里,调用Person构造函数时缺乏了new关键字,此时this值为undefined。因为你不能给undefined添加属性,这段代码抛出了一个错误。在非严格模式下,this会强制转换为全局对象,所以name属性可以被正确赋值为全局变量。 性能
当你作了大量的编码的时候,你很容易在对象中定义了重复的属性或者给函数定义了重复的参数名。严格模式下,这两种状况都会致使错误的发生: 测试
// 严格模式下错误 - 重复参数 function doSomething(value1, value2, value1) { //code } // 严格模式下错误 - 重复属性 var object = { foo: "bar", foo: "baz" };
这二者都是语法错误,在代码执行以前将抛出错误。 this
eval()没有被移除,但它在严格模式下发生了一些变化。最大的改变是:在eval()语句中声明的变量以及函数不会在包含域中建立。例如: 编码
(function() { eval("var x = 10;"); // 非严格模式下,x为10 // 严格模式下,x没有声明,抛出一个错误 alert(x); }());
任意由eval()建立的变量或函数仍呆在eval()里。然而,你能够经过从eval()中返回一个值的方式实现值的传递:
(function() { var result = eval("var x = 10, y = 20; x + y"); // 严格模式与非严格模式下都能正常工做(获得30) alert(result); }());
ECMAScript 5 同时引入了修改属性特征的能力,例如设置一个属性为只读或者冻结整个对象的结构(freezing an entire object’s structure)。在非严格模式下,试图修改一个不可变的属性时将悄无声息地失败。你可能在使用一些原生APIs的时候已经遇到这类问题。严格模式将保证不管你在什么时候试图使用一种不被容许的方式修改一个对象或对象的属性时抛出错误。
var person = {}; Object.defineProperty(person, "name" { writable: false, value: "Nicholas" }); // 非严格模式下将悄无声息地失败,严格模式则抛出错误 person.name = "John";
这个例子中,name属性被设置为只读。在非严格模式下,对name的赋值将悄无声息地失败;而在严格模式下,一个错误将被抛出。
注:若是你在使用ECMAScript属性能力(the ECMAScript attribute capabilities),我强烈推荐你开启严格模式。若是你在改变对象的可变性(mutability of objects),你将遇到一堆错误,而它们在非严格模式下将被安静地带过。
在现代浏览器中很容易启用严格模式,只需添加下面一条语句:
"use strict";
虽然这看起来只是一个没有赋值给变量的字符串,但它确实地指示了Javascript引擎切换为严格模式(那些不支持严格模式的浏览器只是简单地读取了这个字符串而后继续像日常同样运行)。你能够在全局或函数中使用它。话虽这么说,你永远不该该在全局中使用它。全局地使用这条指示,意味着同个文件下的全部代码都在严格模式下运行。
// 别这么作 "use strict"; function doSomething() { // 这将在严格模式下运行 } function doSomethingElse() { // 这也是 }
这看起来彷佛不是个大问题,但在咱们这个不一样脚本堆积在一块儿的世界里(our world of aggressive script concatenation)将致使大麻烦。只要有一份脚本全局地包含这条指令,其它串联的脚本也将在严格模式下运行(可能引起一些你从没预想到的错误)。
所以,最好只在函数内使用严格模式,例如:
function doSomething() { "use strict"; // 严格模式下运行 } function doSomethingElse() { // 非严格模式下运行 }
若是你想将严格模式应用于多个函数,可使用以下模式( immediately-invoked function expression (IIFE)):
(function() { "use strict"; function doSomething() { // this runs in strict mode } function doSomethingElse() { // so does this } }());
我强烈建议每个人都开始使用严格模式。如今已经有足够多的浏览器支持该模式,它将把你从藏身代码的错误中拯救出来。你须要确保你没有全局地包含启用指令,但能够频繁地使用IIFEs给任意多的代码应用严格模式。一开始,你将碰到从没遇过的错误——这是很正常的。切换到严格模式后,你须要作足够多的测试来保证你已hold住你的代码。必定不能只是将“use strict”扔进你的代码而后就假设不会有错误发生。至少的至少,你该开始使用这个异常有用的语言特性来写更好的代码了。