ECMAScript5中引入的严格模式,通过让JavaScript运行环境对一些开发过程中最常见和不易发现的错误做出和当前不同的处理,来让开发者拥有一个”更好”的JavaScript语言。很长一段时间内,由于只有Firefox支持严格模式,我曾对严格模式表示怀疑。但到了今天,所有主流的浏览器都在他们的最新版本中支持了严格模式(包括IE10,Opera12和Android4,IOS5),是时候开始使用严格模式了。
“use strict” 指令在 JavaScript 1.8.5 (ECMAScript5) 中新增。它不是一条语句,但是是一个字面量表达式,在 JavaScript 旧版本中会被忽略。”use strict” 的目的是指定代码在严格条件下执行。严格模式下你不能使用未声明的变量。
严格模式通过在脚本或函数的头部添加 “use strict”; 表达式来声明。大家可以通过浏览器的F12来查看。
严格模式为JavaScript引入了很多变化,我把他们分为两类(明显的和细微的)。细微改进的目标是修复当前JavaScript中的一些细节问题,对于这些问题不在这里进行深入介绍;如果有兴趣,请阅读Dmitry Soshnikov撰写的精彩文档ECMA-262-5 in Detail Chapter 2 Strict Mode。 我在这里主要介绍严格模式引入的明显变化,那些在你使用严格模式前应该知道的概念和那些对你帮助最大的改变。在开始学习具体特性前,请记住严格模式的一大目标是让你能更快更方便的调试。运行环境在发现问题时显性的抛出错误比默不做声的失败或怪异行事(未开启严格模式的JavaScript运行环境经常这样)要好。严格模式会抛出更多错误,但这是好事,因为这些错误会唤起你注意并修复很多以前很难被发现的潜在问题。
Javascript语言的一个特点,就是允许”动态绑定”,即某些属性和方法到底属于哪一个对象,不是在编译时确定的,而是在运行时(runtime)确定的。严格模式对动态绑定做了一些限制。某些情况下,只允许静态绑定,也就是说,属性和方法到底归属哪个对象,在编译阶段就确定。基于上面的语法特点,使用严格模式时会有一些特殊变化。
在普通模式下,为一个未申明的局部变量赋值时会自动创建一个同名全局变量。这是Javacript程序中最容易出现的错误之一, 在严格模式中尝试这么做时会有显性的异常抛出。
"use strict";
x = 3.14; // 报错 (x 未定义)
在函数内部定义变量时,也同样要求必须使用var关键字。
"use strict";
myFunction();
function myFunction() {
y = 3.14; // 报错 (y 未定义)
}
严格模式中另一个重要的变化是函数中未被定义或为空( null or undefined)的this不在默认指向全局环境(global)。这会造成一些依赖函数中默认this行为的代码执行出错,例如:
window.color = "red";
function sayColor() {
alert(this.color);
}
// 在strict模式中会报错, 如果不在严格模式中则提示 “red"
sayColor();
// 在strict模式中会报错, 如果不在严格模式中则提示 “red"
sayColor.call(null);
下面这段对比代码清晰地反映了未定义的this关键字在普通模式和严格模式下的指向区别。
function f(){
return !this;
}
// 返回false,因为"this"指向全局对象,"!this"就是false
function f(){
"use strict";
return !this;
}
// 返回true,因为严格模式下,未定义的this的值为undefined,所以"!this"为true。
严格模式下,this在被明确地赋值之前会一直保持为undefined,这意味着当一个构造函数在执行时,如果之前没有明确的new关键词,就会抛出异常。
function Person(name) {
"use strict";
this.name = name;
}
var me = Person("Nicholas"); //报错,this未定义
在上面的代码中,Person构造函数运行时因为之前没有new,函数中的this会保留为undefined,由于你不能为undefined设置属性,上面的代码会抛出错误。 在非strict模式环境中,没有被复制的this会默认指向window全局变量,运行的结果将是为window全局变量设置了一个name属性,属性值为”Nicholas”。
当编写大量代码时,对象属性和函数参数很容易一不小心被设置成一个重复的名字,严格模式在这种情况下会显性的抛出错误。
function doSomething(value1, value2, value1) { //重复的变量名,在严格模式下会报错
"use strict"
//……
}
对于对象属性名的重复,不同的JS运行环境的处理策略各有不同。测试代码如下:
var fn = function() {
'use strict';
var obj = {
a: 1,
a: 2 // 因为声明的属性比较多,后面添加的属性不小心与已有的属性重复了
};
return obj
};
fn();
当时自己在PC的chrome中测试了程序,运行没问题,也在安卓机上做了测试,也没问题。但在iphone中却报错了,导致页面无法正常渲染。下面我们看下各个运行环境对这段代码的执行情况:
从上面的实验结果可以看出,即使在严格模式下,各个运行环境对部分细节的处理也是不尽相同的。safari和nodejs的严格模式下,对象字面量禁止重复属性声明,而chrome和Firefox则没有这方面的限制。所以即使严格模式下运行通过的代码也不是100%保险的,多做测试还是非常必要的。对于严格模式可帮助防止函数参数名和对象属性名重复这一点,不同的JS运行环境效果不同。
虽然eval()语句在严格模式中没有被移除,但在严格模式中仍然对它进行了一些改进。最大的改变是在eval()中的变量和函数申明只能用于eval语句内部。
正常模式下,Javascript语言有两种变量作用域(scope):全局作用域和函数作用域。严格模式创设了第三种作用域:eval作用域。正常模式下,eval语句的作用域,取决于它处于全局作用域,还是处于函数作用域。严格模式下,eval语句本身就是一个作用域,不再能够生成全局变量了,它所生成的变量只能用于eval内部。例如:
(function() {
eval("var x = 10;");
// 非严格模式中,正常显示10, 严格模式中则因x未被定义而抛出异常,
alert(x);
}());
任何在eval()执行过程中创建的变量或者函数保留在eval()中。但你能明确的从eval()语句的返回值来获取eval()中的执行结果,例如:
(function() {
"use strict"
var result = eval("var x = 10, y = 20; (x + y);");
// 在strict或非strict模式中都能正确的运行余下的语句。resulst为30
alert(result);
}());
ECMAScript5中还引入为对象的特定属性设为只读的语法,或让整个对象不可修改的能力。 但在非严格模式中,尝试修改一个只读属性只会默不做声的失败。 在你和一些浏览器原生API打交道过程中,你很可能遇到这种情况。严格模式会在这种情况下明确的抛出异常,提醒你修改这个属性是不被允许的。
function var person = {};
Object.defineProperty(person, "name",
{
writable: false,
value: "Nicholas"
});
// 在非严格模式时,沉默的失败,在严格模式则抛出异常.
person.name = "John";
上面的例子中,name属性被设为只读,非严格模式中执行对name属性的修改不会引发报错,但修改不会成功。但严格模式则会明确的抛出异常。
由于with语句无法在编译时就确定,属性到底归属哪个对象。严格模式中去除了with语句,包含with语句的代码在严格模式中会抛出异常。所以使用严格模式的第一步:确保你的代码中没有使用with。
function // 在严格模式中以下JavaScript代码会抛出错误
with (location) {
alert(href);
}
严格模式使用标志,将”use strict”放在脚本文件的第一行,则整个脚本都将以”严格模式”运行。如果这行语句不在第一行,则无效,整个脚本以”正常模式”运行。
"use strict"
虽然看上去上面的代码仅仅只是未赋予某个变量的字符串,它实际上起到指示JavaScript引擎切换到严格模式的作用(不支持严格模式的浏览器会忽略以上代码,不会对后续的执行产生任何影响)。虽然你能把这个指令作用到全局或某个函数中,但这里还是要提醒,不要在全局环境下启用严格模式。
// 请不要这么使用
"use strict";
function doSomething() {
// 这部分代码会运行于严格模式
}
function doSomethingElse() {
// 这部分代码也会运行于严格模式
}
虽然上面的代码看起来不算一个大问题。但当你不负责维护页面中引入的全部代码时,这样使用strict模式会让你面临由于第三方代码没有为严格模式做好准备而引发的问题。 因此,最好把开启严格模式的指令作用于函数中,例如:
function doSomething() {
"use strict";
// 这个函数中的代码将会运行于严格模式
}
function doSomethingElse() {
// 这个函数中代码不会运行于严格模式
}
如果你想让严格模式在不止一个函数中开启,请使用立即执行函数表达式
(function() {
"use strict"
function doSomething() {
//这个函数运行于严格模式
var person = {};
Object.defineProperty(person, "name",
{
writable: false,
value: "Nicholas"
});
// 在非严格模式时,沉默的失败,在严格模式则抛出异常.
person.name = "John";
}
function doSomethingElse() {
//这个函数同样运行于严格模式
//在严格模式中以下JavaScript代码会抛出错误
with (location) {
alert(href);
}
}
}());
使用JS的程序员应该总是启用JavaScript严格模式,它能帮你发现代码中未曾注意到的错误。不要在全局环境中启用,但你能尽量多的使用立即执行函数表达式来把严格模式作用到多个函数范围内。一开始,你会遇到之前未曾碰到过的错误提示,这是正常的。当启用严格模式后,请确保已经在支持的浏览器中做了测试,以发现新的潜在问题。一定不要仅仅在代码中添加一行”use strict”就假定余下的代码能正常工作。最后,请在严格模式下开始编写更好的代码。
各款浏览器对严格模式支持情况的汇总统计:点击这里