JavaScript 的 “with” 语句是否有合法用途?

艾伦·斯托姆(Alan Storm)在回应我关于声明的答案时的评论引起了我的思考。我很少找到使用这个特定语言功能的理由,也从未考虑过它如何引起麻烦。现在,我很好奇如何在避免陷阱的同时有效利用 。with

你在哪里发现这个声明有用?with


答案 1

今天我遇到了另一个用途,所以我兴奋地搜索了网络,发现了一个现有的提及:在块范围内定义变量

背景

尽管 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 循环不会引入新作用域,因此所有三个函数都将共享相同的作用域(值为 )。num2

新的范围:和letwith

随着 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 block语法非常相似,但在其他地方并未被广泛采用。


答案 2

我一直在使用 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')
    )
  )
}

对于这个用例,我没有做任何赋值,所以我没有与此相关的歧义问题。