Alan Storm对我关于with声明的回答的评论引起了我的思考。我很少找到使用这个特殊语言特性的理由,也从来没有想过它可能会带来什么麻烦。现在,我很好奇如何有效地利用with,同时避免它的陷阱。
你觉得with语句在哪里有用?
Alan Storm对我关于with声明的回答的评论引起了我的思考。我很少找到使用这个特殊语言特性的理由,也从来没有想过它可能会带来什么麻烦。现在,我很好奇如何有效地利用with,同时避免它的陷阱。
你觉得with语句在哪里有用?
当前回答
我认为对象文字的使用很有趣,就像使用闭包的一个插入式替换
for(var i = nodes.length; i--;)
{
// info is namespaced in a closure the click handler can access!
(function(info)
{
nodes[i].onclick = function(){ showStuff(info) };
})(data[i]);
}
或者与闭包等价的with语句
for(var i = nodes.length; i--;)
{
// info is namespaced in a closure the click handler can access!
with({info: data[i]})
{
nodes[i].onclick = function(){ showStuff(info) };
}
}
我认为真正的风险是不小心操纵不属于with语句的变量,这就是为什么我喜欢将对象文字传递给with,你可以确切地看到它将在代码中添加的上下文中。
其他回答
对于一些简短的代码片段,我想在度模式中使用sin, cos等三角函数,而不是在辐射模式中。为此,我使用了一个AngularDegreeobject:
AngularDegree = new function() {
this.CONV = Math.PI / 180;
this.sin = function(x) { return Math.sin( x * this.CONV ) };
this.cos = function(x) { return Math.cos( x * this.CONV ) };
this.tan = function(x) { return Math.tan( x * this.CONV ) };
this.asin = function(x) { return Math.asin( x ) / this.CONV };
this.acos = function(x) { return Math.acos( x ) / this.CONV };
this.atan = function(x) { return Math.atan( x ) / this.CONV };
this.atan2 = function(x,y) { return Math.atan2(x,y) / this.CONV };
};
然后我可以使用三角函数在度模式没有进一步的语言噪音在一个with块:
function getAzimut(pol,pos) {
...
var d = pos.lon - pol.lon;
with(AngularDegree) {
var z = atan2( sin(d), cos(pol.lat)*tan(pos.lat) - sin(pol.lat)*cos(d) );
return z;
}
}
这意味着:我使用一个对象作为函数的集合,我在有限的代码区域中启用它以便直接访问。我发现这很有用。
实际上,我最近发现with语句非常有用。直到我开始了我目前的项目——一个用JavaScript编写的命令行控制台,我才真正意识到这个技术。我试图模拟Firebug/WebKit控制台api,其中特殊命令可以输入到控制台,但它们不会覆盖全局作用域中的任何变量。当我试图解决我在Shog9的精彩答案的评论中提到的一个问题时,我想到了这个。
为了达到这个效果,我使用了两个with语句将一个作用域“分层”到全局作用域后面:
with (consoleCommands) {
with (window) {
eval(expression);
}
}
这种技术的优点在于,除了性能方面的缺点外,它不会遭受with语句通常带来的恐惧,因为无论如何我们都是在全局作用域中求值——没有伪作用域之外的变量被修改的危险。
让我惊讶的是,当我设法找到在其他地方使用的相同技术时,我受到了启发,发布了这个答案——Chromium源代码!
InjectedScript._evaluateOn = function(evalFunction, object, expression) {
InjectedScript._ensureCommandLineAPIInstalled();
// Surround the expression in with statements to inject our command line API so that
// the window object properties still take more precedent than our API functions.
expression = "with (window._inspectorCommandLineAPI) { with (window) { " + expression + " } }";
return evalFunction.call(object, expression);
}
编辑:刚刚检查了Firebug的源代码,它们将4条语句链接在一起,形成更多的层。疯了!
const evalScript = "with (__win__.__scope__.vars) { with (__win__.__scope__.api) { with (__win__.__scope__.userVars) { with (__win__) {" +
"try {" +
"__win__.__scope__.callback(eval(__win__.__scope__.expr));" +
"} catch (exc) {" +
"__win__.__scope__.callback(exc, true);" +
"}" +
"}}}}";
我认为在将模板语言转换为JavaScript时,with语句可以派上用场。例如base2中的JST,但我更经常看到它。
我同意不使用with语句就可以编程。但因为它不会带来任何问题,所以它是一种合法的使用。
有使用Delphi的经验,我会说使用with应该是最后的大小优化方法,可能由某种javascript最小化算法执行,并访问静态代码分析以验证其安全性。
随意使用with语句可能会遇到范围问题,这是一个非常痛苦的问题,我不希望任何人经历调试会话来弄清楚他…,却发现它捕获了一个对象成员或错误的局部变量,而不是您期望的全局变量或外部作用域变量。
VB的with语句更好,因为它需要点来消除范围的歧义,但Delphi的with语句是一把上膛的枪,带有一发之机,在我看来,javascript的with语句与之相似,足以保证同样的警告。
My
switch(e.type) {
case gapi.drive.realtime.ErrorType.TOKEN_REFRESH_REQUIRED: blah
case gapi.drive.realtime.ErrorType.CLIENT_ERROR: blah
case gapi.drive.realtime.ErrorType.NOT_FOUND: blah
}
归结起来就是
with(gapi.drive.realtime.ErrorType) {switch(e.type) {
case TOKEN_REFRESH_REQUIRED: blah
case CLIENT_ERROR: blah
case NOT_FOUND: blah
}}
你能相信这么低质量的代码吗?不,我们看到它是完全不可读的。这个例子无可否认地证明了没有必要使用with-statement,如果我的可读性是正确的;)