var a = {a: {a: 1}};
function useWith(){
with(a.a){
for(var i = 0; i < 1000000; i++){
a = i;
}
}
}
var b = {b: {b: 1}};
function noWith(){
for(var i = 0; i < 1000000; i++){
b.b.b = i;
}
}
var t1 = new Date().getTime();
useWith()
alert(new Date().getTime() - t1);
var t2 = new Date().getTime();
noWith()
alert(new Date().getTime() - t2);
一般而言,所有写JS的人都有一个通常的概念:“不要用with语句”。这个准则毫无疑问一直是正确的,但要说为什么的话,并不是每个人可以回答的很好。是否去回答这个为什么并无多大意义,因为只须记住结果“不去用”就完全足够。然而去深入的理由还是有的,刚好最近有人这么问起我...刚好自己想总结一下...刚好这个题目作为草稿在博客后台躺了很久...
with语句
with的初衷是为了避免冗余的对象调用:
但其实用变量替换的写法也挺简单:
所以with似乎本来就没有存在的必要。到了如今,会去用with的人才真的是罕见。到了strict模式里,使用with会直接报错:
所以with已经被彻底抛弃,人们甚至都懒的关注理由~
书中的with语句
既然是总结,我想尽可能完整一些,所以就先从书籍开头吧。有关JavaScript的书我书柜里确实不少,比Java要多出60%!好吧,这是个冷笑话。回到正题,关于基础内容的JavaScript的书,我书柜里主要有4本,下面依次拿出来说说:
《JavaScript权威指南》(第五版, David Flanagan, P109):
以现在的眼光看《权威指南》会感觉它说的很啰嗦,一大段内容到最后还来一句相当拗口的话(当然我能理解作为译本这无法避免),到了最后还“我们不做解释”,真是尴尬...
《JavaScript高级程序设计》(第3版, Nicholas C.Zakas, P60):
Zakas的《高级程序设计》是我JavaScript的入坑书,在with语句这个问题上也没有多深入什么,但写的还算简练,并且说法相当委婉。
《JavaScript语言精粹》(修订版, Douglas Crockford, P110),将with语句列为“糟粕”,并用例子讲述了它的不可预料性,结论上:
老道虽也提到了速度,但更着重解释了不可预测性。
《深入理解JavaScript》(Axel Rauschmayer, P153),我手里唯一一本用了超过1页的篇幅来详细解释废弃with的原因的基础参考书。当时买这本书还是为了支持译者非常长,他曾经给我的博客Host提供过帮助,当然其实也想略微吐槽一下此书的翻译错漏:)
此书中的解释是比较全面的,所以我会以此书的解释作为基础,再加上自己的理解,总结在本文。
好了,看了那么多书,下面就进入本文的正题:
为什么不要使用with语句?
总而言之,主要是这几方面的考量:
性能问题
with语句存在显而易见的性能问题,基本上所有的参考书都会提及这一点,但基本不会有什么例子说明。自己可以简单的做一个代码测试,使得对with语句的性能有更直观量化的了解。
简单对一个对象属性赋值100万次,是否使with的结果差距还是很明显:
当然实际使用上极少会用到百万次循环,且损耗在可接受范围内,所以其实性能损失并不是废弃with语句的真正原因。
不可预测性
使用with语句后代码产生的不可预测性是废弃with的根本原因。with强行割裂了词法作用域,将对象临时性地插入到了作用域链中。这使得出现了难以捉摸的代码。
比如最简单的例子:
在这个简单的例子里,字符串"sword"和空对象没有问题,但当传入的参数是带有同名a属性的a对象时,with强行访问了
a.a
这仅仅只是有一个参数的情况下,如果有很多个参数呢?当不知道调用传进来的参数带有何种属性时,多个参数间的各种属性的混乱指向可想而知,这就是“令人惊讶的、和直觉相抵触的行为”的本质。
另外,在with语句中声明的变量,并不属于with指定的参数对象:
在with中声明的变量实际上是被添加到外层的function上的:
这点可能也与想象中有些不同。
仅仅通过标识符及其上下文,无法确定一个with中的标识符指向什么,这是with被废弃的真正原因。它强行混乱了上下文使得程序的预测和解析变得困难,从而产生了之后要说的优化问题。
代码无法优化
由于无法进行预测,代码含义一直在发生变化,不同的调用,或者即使相同的调用也会因为运行时的变化而出现偏差,从而使得代码无法被优化。
优化指两方面,一方面解析和运行变得缓慢,指的就是之前已经谈到的性能。另一方面对于代码优化和压缩工具来说,无法确定是变量还是属性,就不能进行重命名(因为属性无法被重命名)。
结语
游览了几本书,讲了一堆没啥用的内容,我果然很闲...另外,上面那个冷笑话:“JavaScript比Java要多出60%!”指的是字符数量上...好吧,天热了,自己冷自己~