[Effective JavaScript 笔记] 第10条:避免使用with
with特性,提供的任何“便利”都更让其变得不可靠和低效率。
with语句的用法,可以很方便地避免对对象的重复引用。上面的代码整理成下面的形式
function status(info){
var widget=new Widget();
with(widget){
setBackground(‘blue’);
setForeground(‘white’);
setText(‘Status:’+info);
show()
}
}
使用with语句从模块对象中导入变量也很有诱惑力。
function f(x,y){
with(Math){
return min(round(x),sqrt(y));
}
}
在上面的这两种情况,使用with使得提取对象的属性,并将这些属性绑定到块的局部变量中变得非常诱人且容易。
很有吸引力,但没做它们应该做的事。
这两个例子里有两种不同类型的变量。
- 一种是希望引用with对象的属性的变量,如setBackground,round。
- 另一种是希望引用外部变量绑定的变量,如info,x,y。
- 语法上并没有区分这两种类型的变量,都只是看起来像变量。
js中所有变量都是相同的
js从最内层的作用域开始向外查找变量。with语句对待一个对象犹如该对象代表一个变量作用域,因此在with代码块的内部,变量查找从搜索给定的变量名的属性开始。如果在这个对象中没有找到该属性,则继续在外部作用域中搜索。

在ES5规范中这称为词法环境(在旧版本标准中称为作用域链)。
该词法环境的最内层作用域由widget对象提供。接下来的作用域用来绑定该函数的局部变量info和widget。接下来一层绑定到status函数。注意在一个正常的作用域中,会有与局部作用域中的变量同样多的作用域绑定存储在与之对应的环境层级中。但是对于with作用域,绑定集合依赖于碰巧在给定时间点时的对象。
with块中的每个外部变量的引用都隐式地假设在with对象(以及它的任何原型对象)中没有同名的属性。而在程序的其他地方创建或修改with对象或其原型对象不一定会遵循这样的假设。js引擎当然也不会读取局部代码来获取你使用了哪些局部变量。
变量作用域和对象命名空间之间的冲突使得with代码块异常脆弱。如:with对象获得了一个名为info的属性,那么status函数的行为就会被立即改变。status函数将使用这个属性而不是info参数。在源代码的功能进行扩展时,后面有可能决定所有的widget对象都应该有一个info属性。在原型对象上添加了info属性。这将使status函数变得不可预测。
status(‘connecting’);//Status:connecting Widget.prototype.info=”[widget info]”; status(‘connected’);//Status:[widget info]
同样的,如果某人添加名为x或y的属性到Math对象上,那么f函数也悲剧了。
Math.x=0; Math.y=0; f(2,9);//0
总是很难预测一个特定的对象是否已被修改,或是否可能拥有你不知道的属性。事实证明,人力不可预测的特性对于优化编译器同样不可预测。
通常情况下,js作用域可被表示为高效的内部数据结构,变量查找会非常快速。但with代码块需要搜索对象的原型链来查找with代码块里的所有变量,因此,其运行速度远远低于一般的代码块。
在js中没有单个特性能作为一个更好的选择直接替代with语句。在某些情况下,最好的替代方法是简单地将对象绑定到一个简短的变量名上。
function status(info){
var w=new Widget();
w.setBackground(‘blue’);
w.setForebacground(‘white’);
w.addText(‘Status:’+info);
w.show();
}
没有任何变量对于w对象的内容是敏感的。所以即使一些代码修改了Widget的原型对象,status函数的行为依旧是可预期的。
status(‘connecting’);//Status:connecting Widget.prototype.info=”[widget info]”; status(‘connected’);//Status:connected
在其他情况下,最好的方法是显示地将局部变量显式地绑定到相关的属性上。
function f(x,y){
var min=Math.min,round=Math.round,sqrt=Math.sqrt;
return min(round(x),sqrt(y));
}
再次一旦消除with语句,函数的行为变得可以预测了。
Math.x=0; Math.y=0; f(2,9);//2
提示
- 避免使用with语句
- 使用简短的变量名代替重复访问的对象
- 显式地绑定局部变量到对象属性上
[Effective JavaScript 笔记] 第10条:避免使用with的更多相关文章
- [Effective JavaScript 笔记] 第5条:避免对混合类型使用==运算符
“1.0e0”=={valueOf:function(){return true;}} 是值是多少? 这两个完全不同的值使用==运算符是相等的.为什么呢?请看<[Effective JavaSc ...
- [Effective JavaScript 笔记]第28条:不要信赖函数对象的toString方法
js函数有一个非凡的特性,即将其源代码重现为字符串的能力. (function(x){ return x+1 }).toString();//"function (x){ return x+ ...
- [Effective JavaScript 笔记] 第4条:原始类型优于封闭对象
js有5种原始值类型:布尔值.数字.字符串.null和undefined. 用typeof检测一下: typeof true; //"boolean" typeof 2; //&q ...
- [Effective JavaScript 笔记]第27条:使用闭包而不是字符串来封装代码
函数是一种将代码作为数据结构存储的便利方式,代码之后可以被执行.这使得富有表现力的高阶函数抽象如map和forEach成为可能.它也是js异步I/O方法的核心.与此同时,也可以将代码表示为字符串的形式 ...
- [Effective JavaScript 笔记]第46条:使用数组而不要使用字典来存储有序集合
对象属性无序性 js对象是一个无序属性集合. var obj={}; obj.a=10; obj.b=30; 属性a和属性b并没有谁前谁后之说.for...in循环,先输出哪个属性都有可能.获取和设置 ...
- [Effective JavaScript 笔记]第45条:使用hasOwnProperty方法以避免原型污染
之前的43条,44条讨论了属性的枚举,但都没有彻底地解决属性查找中原型污染的问题.看下面关于字典的一些操作 'zhangsan' in dict; dict.zhangsan; dict.zhangs ...
- [Effective JavaScript 笔记]第68条:使用promise模式清洁异步逻辑
构建异步API的一种流行的替代方式是使用promise(有时也被称为deferred或future)模式.已经在本章讨论过的异步API使用回调函数作为参数. downloadAsync('file.t ...
- [Effective JavaScript 笔记]第65条:不要在计算时阻塞事件队列
第61条解释了异步API怎样帮助我们防止一段程序阻塞应用程序的事件队列.使用下面代码,可以很容易使一个应用程序陷入泥潭. while(true){} 而且它并不需要一个无限循环来写一个缓慢的程序.代码 ...
- [Effective JavaScript 笔记]第64条:对异步循环使用递归
假设需要有这样一个函数,接收一个URL的数组并尝试依次下载每个文件直到有一个文件被成功下载.如果API是同步的,使用循环很简单实现. function downloadOneSync(urls){ f ...
随机推荐
- 【MPI学习4】MPI并行程序设计模式:非阻塞通信MPI程序设计
这一章讲了MPI非阻塞通信的原理和一些函数接口,最后再用非阻塞通信方式实现Jacobi迭代,记录学习中的一些知识. (1)阻塞通信与非阻塞通信 阻塞通信调用时,整个程序只能执行通信相关的内容,而无法执 ...
- 定制类自己的的new_handler
C++中的new操作符首先使用operator new函数来分配空间,然后再在此空间上调用类的构造函数构造对象.当operator new无法分配所需的内存空间时,默认的情况下会抛出一个bad_all ...
- 第九章:Javascript类和模块
(过年了,祝大家新年好!) 第6章详细介绍了javascript对象,每个javascript对象都是一个属性集合,相互之间没有任何联系.在javascript中也可以定义对象的类,让每个对象都共享某 ...
- 安装及破解IntelliJ IDEA15
1. 下载安装包 进入 JetBrains 官网,http://www.jetbrains.com/idea/download/#tabs_1=windows, 从Ultimate下载企业版 Inte ...
- java定时器的使用(Timer)
1.在应用开发中,经常需要一些周期性的操作,比如每5分钟执行某一操作等. 对于这样的操作最方便.高效的实现方式就是使用java.util.Timer工具类. private java.util.Tim ...
- easyUI API(version 1.5)
不分先后,只做记录. jquery+easyui培训文档下载地址: 链接: https://pan.baidu.com/s/1dFgFXk9 密码: jj5d 1 easyui-draggable(拖 ...
- tomcat 的安装
安装tomcat的前提: 首先要做的是有java环境,这里我们可以选择安装jre(java环境包).或者说安装java开发工具包ldk 我这里选择安装jdk 我们在google里搜索jdk,你就能都找 ...
- .Net MVC中访问PC网页时,自动切换到移动端对应页面
随着移动端的流行,越来越的网站,除了提供PC网页之外,也提供了移动端的H5页面,手机在访问www.xxx.com的时候,能自动跳转到mobile.xxx.com.网上很多在实现时也能使用JS直接进行跳 ...
- shiro 与 web 的结合
本次使用的jar包为 shiro-core-.jar shiro-web-.jar 从Shiro 1.2开始引入了Environment/WebEnvironment的概念,即由它们的实现提供相应的S ...
- 【UVALive 7334】Kernel Knights
题 题意 有两个队的骑士1到n和n+1到2n,每个骑士只能互相攻击对手队的一个骑士.kernel的意思是在这个kernel里的骑士不会互相攻击,在kernel外的骑士被kernel里的骑士攻击. 现在 ...