【问题标题】:Scope Chain in JavascriptJavascript 中的作用域链
【发布时间】:2010-12-01 19:58:57
【问题描述】:

我正在阅读 Javascript 中的作用域链,但它对我没有任何意义,谁能告诉我作用域链是什么以及它如何与图形一起工作,甚至是白痴也能理解的东西。我用谷歌搜索了它,但我没有找到可以理解的东西:(

【问题讨论】:

    标签: javascript scope-chain


    【解决方案1】:

    ECMAScript(JS 所基于的核心语言)中的任何函数调用都会产生一个单独的执行上下文,它们彼此单独运行。在每个执行上下文中,this 指代有问题的对象,默认为函数附加到的任何对象。

    function foo() {
        alert(this===window)
    }
    

    警告为真,因为窗口是拥有 'foo' 方法的对象。函数中定义的任何变量都可以通过该函数的唯一作用域链环境访问。

    function world() {
        var name = 'global';
        alert(name)
    }
    

    显然会提醒“全球”。

    function world() {
        var name = 'global';
        (function() {
            var name = 'country';
            alert(name)
        })();
        alert(name)
    }
    

    在最新示例中,当调用第一个警报时,Javascript 确定在内部函数的范围链中定义了标识符 name,因此它不必查找范围链来获取它.

    在第二次警报调用中,name 也在相同的上下文中定义并警报“全局”;

    function world() {
        var name = 'global';
        (function() { alert(name) })();
    }
    

    在此示例中,name 标识符未在同一上下文中定义,因此它必须沿作用域链向上移动到定义名称的外部函数,并警告全局。

    参考:

    【讨论】:

      【解决方案2】:

      总结:

      作用域链用于解析javascript中变量名的值。如果没有范围链,如果在不同的范围内定义了多个,Javascript 引擎将不知道为某个变量名称选择哪个值。 javascript 中的作用域链是词法定义的,这意味着我们可以通过查看代码来了解作用域链。

      作用域链的顶端是全局作用域,也就是浏览器中的window对象(NodeJS中的global)。除了全局范围之外,函数还有自己的变量范围。范围链可以通过查看函数的定义位置来确定。

      解析变量时,内部函数首先查看自己的作用域。如果在自己的作用域中找不到变量,它将爬上作用域链并在定义函数的环境中查找变量名。看起来像这样:

      所以在我们的图像示例中,当innerFoo 使用变量bar 时,它首先尝试在innerFoo 的范围内(函数体中的代码)找到它。然后,当它在此处找不到它时,它爬上作用域链foo。在foo 中也没有名为bar 的变量。因此,它将爬上作用域链,现在查看全局作用域。在全局范围中有一个名为 bar 的变量,其值为 10,bar 将被解析为该变量。

      示例:

      let foo = 1;
      let bar = 1;
      
      
      function test (bar) {
         
         return function innerTestFunc () {
            let foo = 10;
            
            console.log(foo, bar);
          }
        
      }
      
      const innerTestFunc1 = test(5);
      const innerTestFunc2 = test(20);
      
      
      innerTestFunc1();  // logs 10, 5
      
      innerTestFunc2();  // logs 10, 20

      在上面的例子中,我们有一个返回函数的函数。我们首先将此函数存储在变量innerTestFunc1innerTestFunc2 中。这会创建一个闭包,它基本上是外部环境范围链的快照。

      然后,当函数执行时,函数需要变量foobar 的值。 foo 的值可以在innerTestFunc 的级别解析,两者都是10。 10 已经在innerFoo 中找到,因此无需为 foo 爬上作用域链。

      对于bar 变量,函数在innerFoo 中找不到它。因此,它将爬上作用域链。它首先在函数test 中遇到变量bar,因此它将bar 的值解析为测试函数中的任何值(在我们的示例中为5、20)。

      【讨论】:

        【解决方案3】:

        Scope chain in Javascript explained in layman terms

        亚历克斯是个快乐的人,在美好的一天,拿着他的月薪走在路上被抢劫了。

        后来他意识到明天是支付女儿 1000 美元学费的最后一天。
        他跑回家,找到了400美元的积蓄,担心剩下的(600美元)。瞬间闪过的念头,是向他父亲马修借一些。
        可怜的木匠马修一无所有,他以 300 美元的价格卖掉了他继承的手镯,并借给了他的儿子亚历克斯。
        Alex在社会上享有良好的声誉,立即从当地银行拿到剩余的300美元,并按时支付女儿的学费。

        回到 Javascript 中的作用域链:
        javascript 中的 Alex-A 函数
        Mathew-立即函数,Alex嵌套在其中。
        Mathews 父级-Mathew 的直接函数嵌套在其中。
        银行全局变量。

        function Bank() {
            loan=300;
            Mathew();
        
            function Mathew() {
                mathew=300;
                Alex();
        
                function Alex() {
                    savings:400;
                    alert('I need some money');
                }
        
            }
        
        }
        
        Bank();
        

        此时 Alex 的作用域链如下所示: [储蓄:400]+[马修:300]+[贷款:300];

        【讨论】:

          【解决方案4】:

          我知道这是一篇旧帖子,但它对开发人员仍然有帮助。我想用一点不同的方式来做这件事,因为它对初学者来说更容易理解范围链。这是我修改后的代码:

          var currentScope = 0; // global scope
          function a () {
             var currentScope = 1, one = 'scope1';
             alert(currentScope);
          
            function b () {
                var currentScope = 2, two = 'scope2';
                alert(currentScope);
          
                function c () {
                   var currentScope = 3, three = 'scope3';
                   alert(currentScope);
            alert(one + two + three); // climb up the scope chain to get one and two
               }
               c();
            }
            b();
          }
          a();
          

          【讨论】:

          • 关于-1,我想知道我的版本是否对“白痴”理解没有帮助(如原始问题所述)。
          • 我不认为你的评论没用。虽然它实际上与第一个示例相同,但我同意第一个示例中的许多立即调用函数表达式可能会让 Javascript 初学者感到尴尬。但是,我会在函数的开头而不是结尾进行函数调用,因为它会使代码更具可读性恕我直言。
          【解决方案5】:

          要了解作用域链,您必须了解闭包的工作原理。

          当你嵌套函数时会形成一个闭包,即使在它们的父函数已经执行之后,内部函数也可以引用它们外部封闭函数中存在的变量。

          JavaScript 通过向上遍历作用域链,从本地移动到全局来解析特定上下文中的标识符。

          考虑这个包含三个嵌套函数的示例:

          var currentScope = 0; // global scope
          (function () {
            var currentScope = 1, one = 'scope1';
            alert(currentScope);
            (function () {
              var currentScope = 2, two = 'scope2';
              alert(currentScope);
              (function () {
                var currentScope = 3, three = 'scope3';
                alert(currentScope);
                alert(one + two + three); // climb up the scope chain to get one and two
              }());
            }());
          }());
          

          推荐阅读:

          【讨论】:

          • 那么,currentScope 3 会覆盖 currentScope 1 的值吗?
          • @Aaron 不是覆盖,而是 shadow
          • @Aaron,正如@kangax 所说,在 third 函数的范围内,在 second、first 和 global上声明的 currentScope 变量> 范围将被简单地 shadowedhidden,它们的值将在外部闭包上保持不变,因为在第三个函数中,您声明了一个 new currentScope 驻留在该范围内的变量,因此当您在那里访问它时,它将是范围链中的第一个。
          • 但是如果我想在第二个函数中访问scopeChain,那么它会是3,因为定义变量的地方而不是执行变量的地方?
          • 我想我明白了,它与我之前的想法不同。让我告诉你我的理解:每个变量实际上都是从定义而不是执行的地方获取值,所以函数有它们的自己的范围和例如currentScope 3 隐藏了 currentScope 2 的值,因为它们是在不同的范围内定义的,但是如果我想在第二个函数中访问 c​​urrentScope 它会返回 2 因为 currentScope 3 只在第三个函数中有效,它只影响在它自己的范围内定义的变量.现在这是有道理的。所以如果我不使用var,那么我恰好定义了一个全局变量
          【解决方案6】:

          这是关于关闭的。您可以在内部范围内使用范围外的变量:

          function get_inner_scope () {
              var outer = 'Outer variable value';
              return function () {
                  alert(outer);
              }
          }
          f = get_inner_scope();
          f(); // alerts Outer variable value
          

          第一个 google 链接提供的其他示例的更多详细信息: http://blogs.msdn.com/jscript/archive/2007/07/26/scope-chain-of-jscript-functions.aspx

          【讨论】:

            猜你喜欢
            • 2017-10-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-02-02
            相关资源
            最近更新 更多