Alan Storm的评论是对我关于with
语句回答的回应,这让我开始思考。我很少找到使用这个特定语言功能的原因,并且从未考虑过它可能会引起麻烦。现在,我想知道如何有效地使用with
,同时避免其缺点。
你在哪里发现with
语句有用?
Alan Storm的评论是对我关于with
语句回答的回应,这让我开始思考。我很少找到使用这个特定语言功能的原因,并且从未考虑过它可能会引起麻烦。现在,我想知道如何有效地使用with
,同时避免其缺点。
你在哪里发现with
语句有用?
今天我想到了另一个用途,因此我兴奋地搜索了一下网络,并找到了现有的提及:在块作用域内定义变量。
尽管 JavaScript 表面上类似于 C 和 C++,但它不会将变量限定于定义它们的块内:
var name = "Joe";
if ( true )
{
var name = "Jack";
}
// name now contains "Jack"
for (var i=0; i<3; ++i)
{
var num = i;
setTimeout(function() { alert(num); }, 10);
}
// variables introduced in this statement
// are scoped to each iteration of the loop
for (let i=0; i<3; ++i)
{
setTimeout(function() { alert(i); }, 10);
}
甚至可以:
for (var i=0; i<3; ++i)
{
// variables introduced in this statement
// are scoped to the block containing it.
let num = i;
setTimeout(function() { alert(num); }, 10);
}
直到ES6普及,这种用法仍然局限于最新的浏览器和愿意使用转译器的开发人员。但是,我们可以使用with
轻松模拟此行为:
for (var i=0; i<3; ++i)
{
// object members introduced in this statement
// are scoped to the block following it.
with ({num: i})
{
setTimeout(function() { alert(num); }, 10);
}
}
let
块语法,但在其他地方没有广泛采用。for (var i = 0; i < 3; ++i) { setTimeout ((function () { var num = i; return function () { alert (num); }; }) (), 10);}
。 - Thomas Edingvar toString = function () { return "Hello"; }; with ({"test":1}) { console.log(toString()); };
。在with语句的作用域中,toString()是Object对象的继承属性,因此明确定义的函数不会被调用。但这仍然是个好回答 :-)。 - Andy E我一直在使用with语句作为一种简单的局部导入方式。假设你有一种标记生成器,而不是编写:
markupbuilder.div(
markupbuilder.p('Hi! I am a paragraph!',
markupbuilder.span('I am a span inside a paragraph')
)
)
你可以改为编写:
with(markupbuilder){
div(
p('Hi! I am a paragraph!',
span('I am a span inside a paragraph')
)
)
}
针对这种使用情况,我没有进行任何赋值操作,因此我不会遇到与之相关的歧义问题。
context.bezierCurveTo
一百次,您可以说 var bc2 = context.bezierCurveTo;
然后每次想调用它时只需输入 bc2(x,x,etc);
。这非常快速,甚至更简洁,而 with
则非常缓慢。 - Jimbo Jonny正如我之前的评论所指出的,无论在任何情况下使用with
都是不安全的,尽管它可能很诱人。由于这个问题没有被直接涵盖在这里,我会重复一下。请考虑下面的代码:
user = {};
someFunctionThatDoesStuffToUser(user);
someOtherFunction(user);
with(user){
name = 'Bob';
age = 20;
}
如果不仔细调查这些函数调用,就没有办法确定程序在此代码运行后的状态。如果user.name
已经设置,那么它现在将会是Bob
。如果没有设置,全局的name
将会被初始化或更改为Bob
,而user
对象将保持没有name
属性。
错误总是会发生。如果你使用with,你最终会这样做,并增加程序失败的可能性。更糟糕的是,你可能会遇到在with
块中设置全局变量的工作代码,无论是故意还是作者不知道这个构造的怪癖。这很像在开关语句中遇到的情况,你不知道作者是否有意这样做,也没有办法知道“修复”代码是否会引入回归。
现代编程语言功能丰富。一些特性,在多年使用后,被发现是不好的,应该避免使用。javascript的with
就是其中之一。
with
语句非常有用。直到开始我的当前项目 - 一个用JavaScript编写的命令行控制台,我从未意识到这种技术。当时我试图模拟Firebug/WebKit控制台API,其中可以在控制台中输入特殊命令,但它们不会覆盖全局范围内的任何变量。当我在评论中提到Shog9的出色答案时,我想到了这一点。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);" +
"}" +
"}}}}";
是的,是的,还有就是这个。它有一个非常合理的用途。看:
with (document.getElementById("blah").style) {
background = "black";
color = "blue";
border = "1px solid green";
}
基本上,任何其他DOM或CSS钩子都是使用with的绝佳方法。这不像“CloneNode”将未定义并返回全局范围,除非您特意决定使其成为可能。
Crockford抱怨速度的原因是with会创建一个新的上下文。上下文通常很昂贵,我同意。但是,如果您只创建了一个div,并且没有现成的框架来设置您的CSS,并且需要手动设置15个左右的CSS属性,则创建上下文可能比变量创建和15个解除引用更便宜:
var element = document.createElement("div"),
elementStyle = element.style;
elementStyle.fontWeight = "bold";
elementStyle.fontSize = "1.5em";
elementStyle.color = "#55d";
elementStyle.marginLeft = "2px";
etc...
with
有很多合法的用途。然而,在这种情况下,你可以这样做:element.style.cssText="background: black ; color: blue ; border: 1px solid green"
。 - GetFreeextend
方法,在一行代码中实现同样的效果:$.extend(element.style, {fontWeight: 'bold', fontSize: '1.5em', color: '#55d', marginLeft: '2px'})
。 - Trevor Burnham.css()
方法。 - nnnnnn你可以定义一个小的辅助函数,以提供with
的好处而不会有歧义:
var with_ = function (obj, func) { func (obj); };
with_ (object_name_here, function (_)
{
_.a = "foo";
_.b = "bar";
});
with_
会导致更多的错误,因为它是(function(_){ _.a="foo"; })(object_here);
的混乱重复版本。(function(_){ _.a="foo"; })(object_here);
是模拟C/Java风格块的标准方法,建议使用该方法代替with_
。 - mk.似乎不值得这样做,因为你可以执行以下操作:
var o = incrediblyLongObjectNameThatNoOneWouldUse;
o.name = "Bob";
o.age = "50";
with
的情况不仅限于长变量名。 - Chris我从不使用 with
,也没有理由使用它,并且不建议使用。
with
的问题在于它防止 ECMAScript 实现执行许多词法优化。随着快速 JIT 引擎的兴起,这个问题在不久的将来可能会变得更加重要。
虽然 with
看起来可以创建更清晰的结构(例如,引入新作用域而不是常见的匿名函数包装器或替换冗长的别名),但它真的不值得。除了性能下降外,还存在将属性分配给错误对象(当注入范围中的对象上未找到属性时)并可能出现错误引入全局变量的风险。我IRC上记得,后者是 Crockford 建议避免使用 with
的原因之一。
with(){}
这样的语句在现代浏览器中的成本,就像其他回答中所给出的那些,我很想看看! - Shog9with(){}
语句的成本,你是完全正确的:在我测试过的每个浏览器中,使用with
建立新作用域的代价都非常高昂。你应该避免在任何经常调用的代码中使用它。此外,在with()
作用域内执行的任何代码都会对Chrome产生明显的影响。有趣的是,在with()
块中的代码在IE中表现最佳: 在IE6和IE8虚拟机中排除设置成本后,with()
提供了最快的成员访问方式(尽管这些虚拟机整体上是最慢的)。谢谢,好东西... - Shog9Visual Basic.NET有一个类似的With
语句。我常用的一种方式是快速设置多个属性。不用写成这样:
someObject.Foo = ''
someObject.Bar = ''
someObject.Baz = ''
我可以编写:
With someObject
.Foo = ''
.Bar = ''
.Baz = ''
End With
这不仅仅是懒惰的问题。它还可以使代码更易读。与 JavaScript 不同的是,它不会出现歧义,因为您必须在语句影响到的所有内容前加上“.
”(点号)。因此,以下两个内容是明显不同的:
With someObject
.Foo = ''
End With
vs.
可以翻译为“对比”、“与”,常用于表示两个事物之间的比较或对抗关系。在IT技术领域中,通常用于表示不同软件、编程语言等之间的比较,例如 Python vs. Java(Python与Java的比较)。With someObject
Foo = ''
End With
前者是someObject.Foo
; 后者是someObject
之外的作用域中的Foo
。
我发现JavaScript缺乏区别,使得它比Visual Basic的variant要不太实用,因为存在歧义的风险太高。除此之外,with
仍然是一个能够提高可读性的强大思想。
with
将对象的内容作为局部变量引入到块中,就像这个小型模板引擎所做的那样。
with
,所以这个东西已经不存在了。 - Thomas Aylott