【问题标题】:Scope resolution in Javascript using 'with' keywordJavascript中使用'with'关键字的范围解析
【发布时间】:2013-05-15 23:25:04
【问题描述】:

使用 'with' 关键字时,Javascript 中的提升和作用域似乎有一种奇怪的行为。

我知道“with”的使用被认为是禁忌,但在其中一个框架中遇到了这个问题并且不得不处理它。不过,

谁能解释为什么如果我们有一个'with'块,声明但未定义的变量是使用with对象解析的,但闭包变量不是这种情况。

要演示,请参见以下代码的输出:http://jsfiddle.net/ftK2Z/1/

var x = 90;

function test1(){
    var address = {
         street: 'Haight',
         aptNum: 2
     };


    with (address){
      alert(aptNum + ":" +  x); // this  outputs 2 : undefined. 
      var aptNum = 100,
      x = 10 ;
    }
}

用谷歌浏览器检查。

注意:我了解 JS 提升,并了解为什么在警报之后使用 var 语句是一个问题,但我想弄清楚的主要问题是, 在警报语句的范围解析期间,由于提升,“aptNum”和“x”都应该在本地范围内未定义,因此它们应该被视为“已声明但未定义”。

但是,aptNum 仍然输出为 '2'。

【问题讨论】:

  • JavaScript 没有块作用域。 with 中的 var aptNum 是封闭函数的局部变量。提供给with 的对象的属性可以作为块内的变量访问,这就是您获得2 的原因。您无法使用var 向对象添加新属性,但您可以在不直接引用对象的情况下更新现有属性。
  • @squint 谢谢。这似乎以某种方式创建了一个块范围,其中“with”对象属性填充在本地范围中。然后范围在块之后消失。但是由于 JS 中没有块作用域,这是一种奇怪的行为
  • 是的,它的行为很奇怪而且令人困惑,这就是人们建议避免它的原因。不过它可能很有用。它是一种范围,但不是典型的变量范围。 var 不受限制。

标签: javascript jquery google-chrome javascript-engine


【解决方案1】:

with 块内,变量aptNumaddress.aptNum 遮蔽,x 指的是局部变量(从不address.x,因为没有这样的属性)。

考虑到这一点并提升,您的代码相当于:

var x = 90;
function test1(){

    var aptNum; // hoisted
    var x;      // hoisted

    var address = {
        street: 'Haight',
        aptNum: 2
    };

    with (address){

        // any reference to aptNum or street inside the with block
        // is actually referencing address.aptNum and address.street

        alert(aptNum + ":" +  x); // this  outputs 2 : undefined. 
                                  // as expected, because there is no address.x
                                  // and the local x is undefined at this point

        aptNum = 100; // this assigns to address.aptNum
                      // the variable aptNum is shadowed by address.aptNum

        x = 10; // this assigns to the local x inside the function 
                // (again because there is no address.x)
    }
}

【讨论】:

  • 所以,基本上,对于 with 块内的 aptNum,它会更新 address.aptNum,但对于 x,它会更新本地的“x”。如果我们检查块外的 x 和 aptNum 的值,这就证明了这一点。 jsfiddle.net/ftK2Z/4。谢谢。这是有道理的。
  • 没错。它还证明了 with 语句是如何令人困惑的。
【解决方案2】:

来自小提琴:

with(address){
    $('#output').text('aptNum : ' + aptNum + ",   x : " + x ); 
    var aptNum = 100,
        x = 10 ;
}

变量x 正在with 语句的主体中重新声明(参见前面的var 关键字)。变量声明被提升,所以 x 被临时定义为 undefined,然后在值已被记录后最终重新定义为 10

在这个更新的小提琴中,我将变量声明语句更改为简单的赋值语句,这会产生将x 的值记录为90 的预期行为。见http://jsfiddle.net/ftK2Z/3/

【讨论】:

  • 我不关心如何让 x 等于 x。实际上,我想在这里展示范围分辨率的特性。问题是为什么 x 与 aptNum 的分辨率不同
  • 这已经通过在x 上显示滥用var 关键字来解释。
【解决方案3】:

不推荐使用 with,并且在 ECMAScript 5 严格模式下被禁止。推荐的替代方法是将要访问其属性的对象分配给临时变量。

它不喜欢有一个全局变量。我认为这是因为 x 超出了地址对象的范围。您可以将参数传递给函数并且它可以工作。

var x = 90;
test(x);

示例:jsFiddle

更多信息请联系JavaScript with

【讨论】:

    【解决方案4】:

    我猜你甚至在声明x 之前就调用了x(这将被视为address 的属性)。 with 范围内的 x 不引用函数外声明的 x。引擎正在尝试读取address.x 的值,当然它不存在。这就是原因。关于with,由于aptNum 被“重新”声明为该范围内的变量,它不引用addressaptNum

    【讨论】:

      【解决方案5】:

      代码

      alert(aptNum + ":" +  x);
      var aptNum = 100,
            x = 10 ;
      

      var-ing aptNumxwith 中。 var 被提升所以也可以认为是

      var aptNum, x;
      alert(aptNum + ":" +  x);
      aptNum = 100, x = 10;
      

      现在很容易看出为什么它们是undefined。你可能不想在这里使用var

      【讨论】:

      • 我明白你的意思。但有趣的是:aptNum 不会输出为“未定义”。它正在解析 address.aptNum 中的值,即 '2'
      猜你喜欢
      • 2013-06-19
      • 1970-01-01
      • 2018-06-30
      • 1970-01-01
      • 2011-09-12
      • 2019-01-12
      • 2022-01-04
      • 1970-01-01
      • 2017-05-07
      相关资源
      最近更新 更多