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


Posted in Javascript onNovember 28, 2014

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

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

严格模式为JavaScript引入了很多变化,我把他们分为两类(明显的和细微的)。细微改进的目标是修复当前JavaScript中的一些细节问题,对于这些问题我不在这里进行深入介绍;如果你有兴趣,请阅读Dmitry Soshnikov撰写的精彩文档ECMA-262-5 in Detail Chapter 2 Strict Mode。 我在这里主要介绍严格模式引入的明显变化,那些在你使用严格模式前应该知道的概念和那些对你帮助最大的改变。

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

去除WITH关键词

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

// 在严格模式中以下JavaScript代码会抛出错误

with (location) {

    alert(href);

}

防止意外为全局变量赋值

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

// 严格模式下会抛出异常

(function() {

    someUndeclaredVar = "foo";

}());

函数中的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属性。

防止重名

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

//重复的变量名,在严格模式下会报错

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);

}());

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

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”就假定余下的代码能正常工作。最后,请在严格模式下开始编写更好的代码。

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

严格模式的优势:

使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等语句中间。

Javascript 相关文章推荐
JQuery的Validation插件中Remote验证的中文问题
Jul 26 Javascript
JavaScript 错误处理与调试经验总结
Aug 10 Javascript
浅析JavaScript中的typeof运算符
Nov 30 Javascript
node.js中Socket.IO的进阶使用技巧
Nov 04 Javascript
jquery操作select方法汇总
Feb 05 Javascript
如何使用jquery修改css中带有!important的样式属性
Apr 28 Javascript
浅析JavaScript的几种Math函数,random(),ceil(),round(),floor()
Dec 22 Javascript
selenium 与 chrome 进行qq登录并发邮件操作实例详解
Apr 06 Javascript
angular6.x中ngTemplateOutlet指令的使用示例
Aug 09 Javascript
微信小程序日历插件代码实例
Dec 04 Javascript
vue中keep-alive、activated的探讨和使用详解
Jul 26 Javascript
使用AutoJs实现微信抢红包的代码
Dec 31 Javascript
jquery+php实现搜索框自动提示
Nov 28 #Javascript
Javascript前端UI框架Kit使用指南之kitjs事件管理
Nov 28 #Javascript
Javascript前端UI框架Kit使用指南之kitjs的对话框组件
Nov 28 #Javascript
Javascript前端UI框架Kit使用指南之Kitjs简介
Nov 28 #Javascript
非jQuery实现照片散落桌子上,单击放大的LightBox效果
Nov 28 #Javascript
开源的javascript项目Kissy介绍
Nov 28 #Javascript
对比分析json及XML
Nov 28 #Javascript
You might like
再次研究下cache_lite
2007/02/14 PHP
php 时间计算问题小结
2009/01/04 PHP
windows的文件系统机制引发的PHP路径爆破问题分析
2014/07/28 PHP
让ThinkPHP支持大小写url地址访问的方法
2014/10/31 PHP
通过修改配置真正解决php文件上传大小限制问题(nginx+php)
2015/09/23 PHP
ThinkPHP中order()使用方法详解
2016/04/19 PHP
浅谈PHP命令执行php文件需要注意的问题
2016/12/16 PHP
高效的表格行背景隔行变色及选定高亮的JS代码
2010/12/04 Javascript
框架页面高度自动刷新的Javascript脚本
2013/11/01 Javascript
js模仿hover的具体实现代码
2013/12/30 Javascript
JavaScript中提前声明变量或函数例子
2014/11/12 Javascript
jQuery trigger()方法用法介绍
2015/01/13 Javascript
如何用jQuery实现ASP.NET GridView折叠伸展效果
2015/09/26 Javascript
浅析AngularJs HTTP响应拦截器
2015/12/28 Javascript
RequireJS使用注意细节
2016/05/15 Javascript
Select2.js下拉框使用小结
2016/10/24 Javascript
vue.js 获取当前自定义属性值
2017/06/01 Javascript
Vue.js结合Ueditor富文本编辑器的实例代码
2017/07/11 Javascript
纯javascript前端实现base64图片下载(兼容IE10+)
2018/09/14 Javascript
Node.js模拟发起http请求从异步转同步的5种用法
2018/09/26 Javascript
详解js中的几种常用设计模式
2020/07/16 Javascript
Python fileinput模块使用实例
2015/06/03 Python
详解Django中的form库的使用
2015/07/18 Python
Python进阶篇之字典操作总结
2016/11/16 Python
Python 12306抢火车票脚本
2018/02/07 Python
python学习基础之循环import及import过程
2018/04/22 Python
Python 实现异步调用函数的示例讲解
2018/10/14 Python
将pandas.dataframe的数据写入到文件中的方法
2018/12/07 Python
python 将大文件切分为多个小文件的实例
2019/01/14 Python
django配置连接数据库及原生sql语句的使用方法
2019/03/03 Python
python实现的按要求生成手机号功能示例
2019/10/08 Python
Python3爬虫中识别图形验证码的实例讲解
2020/07/30 Python
《称象》教学反思
2014/04/25 职场文书
党员个人对照检查材料
2014/10/01 职场文书
药品开票员岗位职责
2015/04/15 职场文书
毕业生入职感言
2015/07/31 职场文书