使用() - 语句在JavaScript中局部变量的行为

时间:2010-04-10 10:23:50

标签: javascript jquery

我注意到一些奇怪的(根据我的知识未定义的行为,至少通过ECMA 3.0规范),请看下面的片段:

var foo = { bar: "1", baz: "2" };
alert(bar);
with(foo) {
    alert(bar);
    alert(bar);
}
alert(bar);

它在Firefox和Chrome中崩溃,因为第一个alert()中不存在“bar”;声明,这是预期的。但是如果你在with() - 语句中添加一个bar声明,那么它看起来像这样:

var foo = { bar: "1", baz: "2" };
alert(bar);
with(foo) {
    alert(bar);
    var bar = "g2";
    alert(bar);
}
alert(bar);

它将产生以下内容:

undefined, 1, g2, undefined

好像你在with() - 语句中创建了一个变量,大多数浏览器(在Chrome或Firefox上测试过)都会使该变量存在于该范围之外,它只是设置为undefined。现在从我的角度来看bar应该只存在于with() - 语句中,如果你让这个例子更加奇怪:

var foo = { bar: "1", baz: "2" };
var zoo;
alert(bar);
with(foo) {
    alert(bar);
    var bar = "g2";
    zoo = function() {
        return bar;
    }
    alert(bar);
}
alert(bar);
alert(zoo());

它会产生这个:

undefined, 1, g2, undefined, g2

所以with() - 语句中的bar在它之外不存在,但运行时以某种方式“自动地”创建一个名为bar的变量,该变量在其顶级范围(全局或函数)中未定义但是这个变量不是指with() - 语句中的变量,只有当with() - 语句有一个名为bar的变量定义时,该变量才会存在。< / p>

非常奇怪,不一致。有人对这种行为有解释吗? ECMA规范中没有任何关于此的内容。

2 个答案:

答案 0 :(得分:1)

以下是ECMA-262,第3版,§12.2,

  

输入执行范围时会创建变量。 未定义新的执行范围。

  

执行 VariableStatement 时,为 Initialiser 的变量分配其 AssignmentExpression 的值,而不是在创建变量时。< / p>

这意味着您必须将带有初始化程序的声明视为两个单独的步骤:首先,在进入函数(执行脚本)时在函数本地(全局)范围内创建一个新变量,但是您没有在到达声明之前分配一个值。在您的示例中,此时范围链由于with()而发生了更改,并且赋值会看到重载的varriable,而新创建的undefined

这意味着

with({ foo : 42 }) {
    var foo = 'bar';
}

被解析为

with({ foo : 42 }) {
    var foo;
    foo = 'bar';
}

相当于

var foo;
({ foo : 42 }).foo = 'bar';

因此保留foo未初始化。

答案 1 :(得分:0)

我认为你只是误解了范围。不是我责备你,如果你来自大多数其他语言它会让你去“WTF?”。 Javascript有我称之为“FUBAR范围”(我确信这里有一个合适的术语,但我从不费心去学习它......知道它是如何工作的&gt;知道它叫什么,至少对我而言)。

Let's make the example even simpler

with({}) {
    var foo = "test";​
}
alert(foo); //alerts "test"

with()允许变量的块级定义,以便传递到作用域(good answer demonstrating this here)。但是......它不会将变量限制在此范围内,之后可以使用here's a better example

with({ bar: 1}) {
    this.foo = "test";
    alert(bar); //alerts: 1"1"
    alert(this); //alerts: "DOMwindow"
}
alert(foo);

如果with()是您正在处理的元素的闭包,那么this将引用我们传入with的{ bar: 1}对象,但它不是:) 。在这种情况下,this仍然是window的全局背景。由于您仍然在此上下文中执行,因此您在该上下文中定义的任何变量都将在该上下文中可用,因此在with()之后的函数中可用,因为它们被定义为更高范围超出你的想象。 Intuition告诉你它是在一个更受限制的范围内定义的:在with() ...但是javascript不同,它都取决于你的闭包运行的上下文。

我试图找到这种行为的良好描述,它提供了更多的范围示例this is the best I could find for you。总的来说,这更像是一个明确的闭包/范围问题,这个网站更详细地涵盖了整体概念,以防我做了一个糟糕的工作来解释它...我知道它在许多方面是一个倒退的概念,遗憾!