Alan Storm对我关于with声明的回答的评论引起了我的思考。我很少找到使用这个特殊语言特性的理由,也从来没有想过它可能会带来什么麻烦。现在,我很好奇如何有效地利用with,同时避免它的陷阱。
你觉得with语句在哪里有用?
Alan Storm对我关于with声明的回答的评论引起了我的思考。我很少找到使用这个特殊语言特性的理由,也从来没有想过它可能会带来什么麻烦。现在,我很好奇如何有效地利用with,同时避免它的陷阱。
你觉得with语句在哪里有用?
当前回答
我从来没有使用过,没有理由,也不推荐它。
with的问题是它阻止了ECMAScript实现可以执行的大量词汇优化。随着基于jit的快速引擎的兴起,这个问题在不久的将来可能会变得更加重要。
它可能看起来像允许更清晰的结构(例如,引入一个新的作用域而不是公共的匿名函数包装器或替换冗长的别名),但实际上不值得这样做。除了性能下降之外,还存在给错误对象的属性赋值的危险(当在注入作用域中的对象上找不到属性时),以及可能错误地引入全局变量。IIRC,后一个问题是促使Crockford建议避免的问题。
其他回答
使用“with”可以使代码更加枯燥。
考虑下面的代码:
var photo = document.getElementById('photo');
photo.style.position = 'absolute';
photo.style.left = '10px';
photo.style.top = '10px';
你可以把它晾干到以下程度:
with(document.getElementById('photo').style) {
position = 'absolute';
left = '10px';
top = '10px';
}
我想这取决于你是更喜欢易读性还是表达性。
第一个示例更容易读懂,可能推荐用于大多数代码。但是大多数代码都很平淡。第二种方法稍微晦涩一些,但它利用语言的表达特性来减少代码大小和多余的变量。
我想喜欢Java或c#的人会选择第一种方式(object.member),而喜欢Ruby或Python的人会选择后者。
Visual Basic。NET有一个类似的With语句。我使用它的一个比较常见的方法是快速设置一些属性。而不是:
someObject.Foo = ''
someObject.Bar = ''
someObject.Baz = ''
,我可以这样写:
With someObject
.Foo = ''
.Bar = ''
.Baz = ''
End With
这不仅仅是懒惰的问题。它还使代码可读性更强。与JavaScript不同的是,它不会出现歧义,因为您必须在受语句影响的所有内容前加上a。(点)。所以,下面两个是明显不同的:
With someObject
.Foo = ''
End With
vs.
With someObject
Foo = ''
End With
前者是someObject.Foo;后者是someObject作用域外的Foo。
我发现JavaScript缺乏区分使得它远不如Visual Basic的变体有用,因为歧义的风险太高了。除此之外,with仍然是一个强大的想法,可以提高可读性。
with语句可用于减少代码大小或用于私有类成员,例如:
// demo class framework
var Class= function(name, o) {
var c=function(){};
if( o.hasOwnProperty("constructor") ) {
c= o.constructor;
}
delete o["constructor"];
delete o["prototype"];
c.prototype= {};
for( var k in o ) c.prototype[k]= o[k];
c.scope= Class.scope;
c.scope.Class= c;
c.Name= name;
return c;
}
Class.newScope= function() {
Class.scope= {};
Class.scope.Scope= Class.scope;
return Class.scope;
}
// create a new class
with( Class.newScope() ) {
window.Foo= Class("Foo",{
test: function() {
alert( Class.Name );
}
});
}
(new Foo()).test();
如果你想要修改作用域,with语句是非常有用的,这对于拥有自己的全局作用域是必要的,可以在运行时操作。你可以在它上面放置常量或者某些常用的辅助函数,比如:“toUpper”,“toLower”或“isNumber”,“clipNumber”麻生..
关于糟糕的性能,我经常读到:作用域函数不会对性能产生任何影响,事实上,在我的FF中,有作用域的函数比无作用域的函数运行得更快:
var o={x: 5},r, fnRAW= function(a,b){ return a*b; }, fnScoped, s, e, i;
with( o ) {
fnScoped= function(a,b){ return a*b; };
}
s= Date.now();
r= 0;
for( i=0; i < 1000000; i++ ) {
r+= fnRAW(i,i);
}
e= Date.now();
console.log( (e-s)+"ms" );
s= Date.now();
r= 0;
for( i=0; i < 1000000; i++ ) {
r+= fnScoped(i,i);
}
e= Date.now();
console.log( (e-s)+"ms" );
所以在上面提到的方式中使用with语句对性能没有负面影响,但它减少了代码大小,这影响了移动设备上的内存使用。
CoffeeScript的Coco fork有一个with关键字,但它只是简单地将this(在CoffeeScript/Coco中也可以写成@)设置为块中的目标对象。这消除了歧义,实现了ES5严格模式兼容:
with long.object.reference
@a = 'foo'
bar = @b
使用with还会使代码在许多实现中变慢,因为现在所有内容都被包装在一个额外的查找范围中。在JavaScript中使用with没有合理的理由。