【问题标题】:How to access a variable from outer scope in JavaScript如何在 JavaScript 中从外部范围访问变量
【发布时间】:2023-04-07 10:57:01
【问题描述】:

在下面的示例中,我尝试访问位于函数外部的x

我希望得到 20 作为输出,但输出是 undefined

谁能解释为什么会这样,有没有办法访问外部 x?

var x = 10;
function outer() {
    var x = 20;
    function inner() {
        var x = 30;
        function printX() {
            console.log(outer.x);
        }
        printX();
    }
    inner();
}
outer();

【问题讨论】:

  • 使用三个不同的变量名而不是 x 三次,它将是可访问的。但是,如果您使用相同的名称,那么您会阻止自己访问除最近的名称之外的任何内容。
  • 你是不是想说在上面的例子中,不能在内部函数中访问外部x?
  • 正确。您编写代码的方式使内部函数无法访问外部变量。
  • @Makedonmakedonski 如果你想要一个正式的术语,它叫做variable shadowing
  • 这根本不是变量的工作方式。

标签: javascript scope


【解决方案1】:

作用域的设计与 JavaScript 不同。为了将变量 x 附加到其范围,您需要通过名称或此引用来引用该范围的对象。

您的示例中发生的情况是您对 printX 的调用尝试记录附加到函数外部的变量 x。函数派生自 JavaScript 中的对象,因此它们可能附加了属性,因此不会给您一个引用错误,而是会得到 undefined,因为变量不存在。

有关范围界定的更多信息,请参阅我的answer on scope in JavaScript

var x = 10; // Globally scoped variable named "x"
function outer() {
    var x = 20; // Locally scoped to outer function variable named "x"
                // in outer function, this variable takes precedence over the
                // globally scoped x which was 10
    function inner() {
        var x = 30; // Locally scoped to inner function variable named "x"
                    // in inner function, this variable takes precedence over the
                    // parent scoped x which was 20
        function printX() {
            console.log(outer.x); // Tries to read "x" property of the outer function
                                  // If this had been console.log(x) it would give 30 because it is scoped to the function inner's variable environment
        }
        printX();
    }
    inner();
}
outer();

至于接下来要做什么,这真的取决于最终目标是什么。正如 cmets here 中所指出的,解决此问题的简单方法是简单地重命名变量。但是,这仍然不能解决尝试通过属性名称而不是变量名称访问变量的主要问题。要按名称访问变量,只需使用其名称(如果它们共享范围,则区分名称),而不是尝试访问在这种情况下不存在的属性名称。

【讨论】:

    【解决方案2】:

    由于此处尚未提及,我将添加另一种可能的方法,即利用this 和通过.apply() 调用函数的范围,如下所示:

    var x = 10;
    function outer() {
        var x = 20;
        function inner() {
            var x = 30;
            function printX() {
                // this now contains all 3 x variables without adding any parameters to any of the functions
                console.log("Window x:", this.windowX);
                console.log("Outer x:", this.outerX);
                console.log("Inner x:", this.innerX);
            }
            // pass through existing context (which we got from inner.apply(...) down below, as well as add
              // inner() x value to the new context we pass to printX() 
            printX.apply({...this, innerX: x});
        }
        // pass through existing context (which we got from outer.apply(...) down below, as well as add
          // outer() x value to the new context we pass to inner()
        inner.apply({...this, outerX: x});
    }
    // pass through window level x as "this" to outer(). Technically it's still available via window.x,
    // but this will be consistent with the others
    outer.apply({windowX: x});

    【讨论】:

      【解决方案3】:

      您创建的不是对象属性,而是内部变量。您还对其进行遮蔽(即您在内部范围内定义另一个具有相同名称的名称),以便您无法访问它们。

      基本上你可以访问outer.x,但你没有设置它(只有一个名为x的函数作用域变量)。并回答你的问题“如果你能得到那个变量”:不抱歉。因为你已经通过定义一个同名的内部变量来隐藏它。

      你可以这样做:

      var x = 10;
      function outer() {
          outer.x = 20;
          function inner() {
              inner.x = 30;
              function printX() {
                  console.log(outer.x);
              }
              printX();
          }
          inner();
      }
      outer();
      

      但这只会使其他变量无用,并且仅仅因为你可以在函数上设置变量不是最佳做法。

      继续学习。

      【讨论】:

        【解决方案4】:

        您可以看一下范围的概念以获得更清晰的信息,但第一个 x 在全局范围内并且可以在函数内访问,但是您将变量值重新分配为20 内外功能。如果您在外部函数中控制台记录 x 的值,但不在内部函数内部,则结果将为 20。您在内部函数中将值 30 分配给 x,因此当您在内部函数中访问 x 时,它将是 30。如果你在每个位置使用 console.log(x),你会看到不同的结果。

        var x = 10;
        
        function outer() {
            var x = 20;
        
        function inner() {
            var x = 30;
        
            function printX() {
                console.log(x);
            }
        
            printX();
        }
        
        inner();
        console.log(x);
        

        }

        外部(); 控制台.log(x);

        【讨论】:

          猜你喜欢
          • 2011-06-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-02-14
          • 1970-01-01
          • 1970-01-01
          • 2016-06-23
          • 1970-01-01
          相关资源
          最近更新 更多