Javascript 与';与';声明和电话

Javascript 与';与';声明和电话,javascript,with-statement,ecma262,Javascript,With Statement,Ecma262,结果未定义,30,20 如果您能以逐步调试的方式解释这一工作原理,我们将不胜感激。通常建议您。真让人困惑 也就是说,如果我们只对代码进行注释,这可能是最简单的。我将您的匿名对象称为{},而不是this,以避免歧义。这里的顺序切换仅仅是为了按照从上到下的执行顺序读取代码 var a = ({ x: 10, foo: function () { function bar() { console.log(x); conso

结果未定义,30,20


如果您能以逐步调试的方式解释这一工作原理,我们将不胜感激。

通常建议您。真让人困惑

也就是说,如果我们只对代码进行注释,这可能是最简单的。我将您的匿名对象称为
{}
,而不是
this
,以避免歧义。这里的顺序切换仅仅是为了按照从上到下的执行顺序读取代码

var a = ({
    x: 10,
    foo: function () {
        function bar() {
            console.log(x);
            console.log(y);
            console.log(this.x);
        }
        with (this) {
            var x = 20;
            var y = 30;
            bar.call(this);
        }
    }
}).foo();

清澈如泥?(如果可以避免的话,不要将
一起使用!)

好的,让我们先简化一下代码。我已经重构出
foo
是一种方法,它不需要演示意外行为

var a = ({
    x: 10,
    foo: function () {

        // entering `with(this)`: all variables are searched against `{}`
        // before the engine attempts to create a new variable:
        with (this) {

            // `var x` creates a local `x`. But, during assignment,
            // `x` matches `{}.x`, so `{}.x` is set. **local `x`** 
            // remains `undefined`.
            var x = 20;

            // `y` isn't found in `{}`, so it's a local variable
            var y = 30;

            // execute `bar()`
            bar.call(this);
        }

        // we're now in scope of `{}.foo()`, but not `with(this)`.
        function bar() {

            // local variable `x` was declared, but never defined.
            console.log(x);

            // local variable `y` exists in the scope of `{}.foo()`
            console.log(y);

            // we're still in the "macro" scope of `{}`. So, `this` refers
            // to `{}`, which was changed to 20.
            console.log(this.x);
        }

    }
}).foo();
那么,当我们调用
foo
时会发生什么呢

  • 设置并填充声明的函数和变量。这就是所谓的“吊装”。在
    foo
    中,有函数
    bar
    和变量
    x
    y
    (函数
    foo
    本身及其参数
    a
    ,仅在我的重构版本中)。这是
    bar
    可以访问的范围
  • 执行该命令。它将当前词汇环境与基于
    a
    对象的词汇环境进行交换,该对象的任何属性现在都可以像变量一样访问
  • 20
    分配给
    x
    。这是什么
    x
    ?解析该标识符时,将选中
    a
    对象,-oh-它具有与该名称的绑定!因此我们将值放入该绑定中,这将把
    20
    放在对象的
    .x
    属性上
  • 30
    分配给
    y
    。这是什么?再次检查当前词汇环境,但在
    a
    对象上找不到
    y
    属性。因此,我们继续讨论父环境,它包含上面创建的
    x
    y
    bar
    变量。实际上,这里我们找到了一个
    y
    变量,因此我们将值
    30
    放在该插槽中
  • 调用
    函数。再次,设置一个新的执行上下文(如上所述),其中步骤1中的一个作为其父范围(由
    bar
    foo
    中的词法位置确定,此时
    bar
    函数被实例化-词法闭包)。现在,我们记录这三份意向书:
    • x
      解析为
      foo
      范围内的变量
      x
      ,该变量的值仍为
      未定义
    • y
      解析为
      foo
      范围内的变量
      y
      ,该变量保存我们刚刚分配的值
      30
    • a.x
      解析为
      a
      对象的
      x
      属性,该属性保存我们刚刚分配的值
      20

  • 实际上,有一个
    x
    变量(虽然不是
    bar
    的局部变量)。@Bergi它不是局部变量。这就是重点。由于
    with
    ,它是对象的属性。它是
    foo
    的局部变量。检查我的答案以了解解释:-)你可以让
    使用严格模式,它不会抛出任何错误。@Bergi我可能在术语上弄错了;但我认为这是一个标识符;不是一个变量。它实际上是
    未定义的
    @svidgen:它也适用于变量声明。在任何
    var xyz=123
    类型的操作中,
    var xyz
    部分被提升,但赋值保持不变。坦率地说,我不能理解x==undefined的意义,我的意思是我知道当
    with
    语句终止时,它会删除它作为扩展范围对象操作的对象的所有更改,但是在我们的例子中,我们不终止它,我们继续从我们未命名的对象调用一个本地方法,这有点让人困惑…Bergi:也许值得重写代码,将提升机放在它们的最终目的地。“当with语句终止时,它会删除它所操作的对象的所有更改”-不,你怎么会这样认为?顺便说一句,将
    bar()
    调用移出带有
    语句的
    也没有什么区别。不完全是这样。它将词法环境更改回原始环境,是的,但不会恢复对这两个环境所做的修改。使用
    所“插入”的环境是原始环境的子环境,并代理在对象上找到的属性。看这可能会有点帮助:
    
    function foo(a) {
        // var x, y, bar - hoisting
        function bar() {
            console.log(x);
            console.log(y);
            console.log(a.x);
        }
        with (a) {
            var x = 20;
            var y = 30;
            bar();
        }
    }
    foo({x:10});