JavaScript的“with”语句是否有合法用途?
艾伦风暴的响应我的回答有关意见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);
}
由于for循环没有引入新的作用域,因此所有三个函数都会共享相同的num
- 值为2
。
一个新的范围: let
和with
通过在ES6中引入let
语句,在必要时引入一个新范围以避免这些问题变得很容易:
// 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);
}
}
循环现在按预期工作,创建三个单独的变量,其值在0到2之间。请注意,在块内声明的变量没有作用域,与C ++中块的行为不同(在C中,变量必须在一个块,所以它是相似的)。 这种行为实际上与早期版本的Mozilla浏览器中引入的let
语法非常相似,但在其他地方尚未广泛采用。
我一直使用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')
)
)
}
对于这个用例,我没有做任何任务,所以我没有与之相关的模糊问题。
正如我先前的评论指出,我不认为你可以使用with
安全无论多么诱人它可能在任何给定的情况。 由于这里没有直接涉及这个问题,所以我会重复一遍。 考虑下面的代码
user = {};
someFunctionThatDoesStuffToUser(user);
someOtherFunction(user);
with(user){
name = 'Bob';
age = 20;
}
在仔细研究这些函数调用的情况下,在代码运行后无法确定程序的状态。 如果user.name
已经设置,它现在将是Bob
。 如果未设置,全局name
将被初始化或更改为Bob
,并且user
对象将保持不带name
属性。
错误发生。 如果你和你一起使用,最终会这样做,并增加你的程序失败的几率。 更糟糕的是,你可能会遇到工作代码,它会在block中设置一个全局的地方,或者故意地或者通过作者不知道构造的这个怪癖。 这很像在交换机上遇到问题,你不知道作者是否打算这样做,也无法知道“修复”代码是否会引入回归。
现代编程语言充斥着各种功能。 经过多年使用后,有些功能被发现是不好的,应该避免。 Javascript的with
是其中之一。
上一篇: Are there legitimate uses for JavaScript's "with" statement?
下一篇: What does it mean that Javascript is a prototype based language?