let块语句和等价的with语句有什么区别?

19

已废弃

在ES6最终确定之前,let语句的块级版本被删除了,并且支持它的浏览器也已将其删除。这个问题现在只具有历史意义。

使用ECMAScript 6 let块级语句和使用等效对象文字的with语句是否有区别?

使用let语句

var x = 10;
let (x = x * 10,
     y = x + 5) {
    console.log("x is " + x + ", y is " + y);
}

使用 with 语句

var x = 10;
with ({x: x * 10,
       y: x + 5}) {
    console.log("x is " + x + ", y is " + y);
    // writes "x is 100, y is 15"
}

1
你是在调侃,还是认真考虑使用 with 进行作用域限定? - Ruan Mendes
1
如果答案明确是“没有差异”,那么我会考虑它,但主要是因为我期望有差异,但却找不到。 - Jeremy
3个回答

8
您可以使用withlet语句来实现相同的目标,但我看到这里有两个显著的区别。最终,let语句是with语句的新版本,消除了后者的缺点。 性能:在with语句的情况下,您将一个额外的JavaScript对象添加到作用域链中。这不是一个小的代价,您必须记住对象具有潜在的长原型链,因此要查找变量,JavaScript引擎首先必须搜索对象及其所有原型。另一方面,对于let语句,引擎只需要搜索最多一个附加对象。let语句确实可以在没有任何开销的情况下实现,因为在let语句中声明的所有变量都已在编译时知道,JavaScript引擎可以轻松地优化代码,例如通过基本上将您的示例视为:
var x = 10;
var let1x = x * 10;
var let1y = x + 5;
{
    console.log("x is " + let1x + ", y is " + let1y);
}

代码可读性: 如上所述,let语句始终使所有声明在编译时可见,这可以防止出现以下代码:

with (foo)
{
    console.log("x is " + x + ", y is " + y);
}

如果你看上面的代码,xy是什么?它们是函数变量还是对象foo的属性?如果不知道foo是什么,就无法确定 - 并且对于同一函数的不同调用可能会有所不同。这就是为什么with语句已被弃用的主要原因。虽然可以像在问题中所做的那样使用它(这很好),但也允许非常可疑和难以阅读的代码结构。而let语句则不会 - 有时候,更少的灵活性是一种优势。

7
我能提供的最佳翻译是,with 语句还会泄露 Object 原型链上的任何属性:
with ({x: 10}) {
    hasOwnProperty = 3;
    console.log(hasOwnProperty);  // 3
}
console.log(hasOwnProperty);  // [native code]; this is window.hasOwnProperty

实际上不太可能成为问题,但仍然存在潜在的陷阱。

我还怀疑with比词法更慢,因为它添加了另一个必须被搜索的命名空间。

老实说,我会避免使用这两种结构;with风格的隐式属性访问对我来说不太合适,如果我真的需要像那样紧密的作用域,一个裸块内部的let表达式读起来比let块不那么尴尬。


请注意,ECMAScript 5 允许我们创建一个没有原型的对象,方法是使用 Object.create(null),因此似乎可以在没有副作用的情况下使用它。但是,ES5 的严格模式禁用了 with,因此这只在 ES5 的默认模式下可行,在 ES6 中将不再可能。 - Jeremy
参见 Was there a way to create an object without a prototype prior to ES5?,其中提供了一个 ES3 可能的(尽管非常不切实际)替代方案。 - Jeremy

1
以下是每个语句的不同作用域规则。
使用 with

with 语句使得对命名引用的访问效率低下,因为这种访问的作用域在运行时无法计算。

使用 let

使用 let 定义的变量的作用域是 let 块本身以及其中包含的任何内部块,除非这些块通过相同的名称定义变量。

let 语句是非标准的,而 with 语句在严格模式下不可用。 参考资料

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接