• 深度理解作用域链和闭包


    当函数被调用的时候,一个叫做actvation object的特别对象被创建。这个对象被传入实参和特别的arguments对象。然后,这个actvation object被用作函数的变量对象,供函数引用对象的值

    actvation object存储实参,arguments对象,函数声明,但不存储函数表达式。

    function foo(x, y) {
      var z = 30;
      function bar() {} // FD
      (function baz() {}); // FE
    }
     
    foo(10, 20);

    在这段代码foo函数拥有如下的actvation object对象。

    再一次提醒函数表达式(FE)不会被存储。

    作用域链

    作用域链是为了查找变量,规则很简单:如果在当前作用域(activation object)中找不到一个变量,就向上在父变量对象中查找。如此反复。

    当函数定义的时候,会创建一个[[Scope]]属性。和activation object不同,它存储的是其父函数的作用域链。

    而先前也提到,当函数调用的时候会创建一个activetion object 变量对象。

    当最终函数执行时,可访问的变量实际在一个叫作用域链东西中寻找,这个作用域链实际就是 activetion object( 自身变量对象)和 [[Scope]](之前的作用域链)。

    分析下面代码

    var x = 10;
     
    (function foo() {
      var y = 20;
      (function bar() {
        var z = 30;
        // "x" and "y" are "free variables"
        // and are found in the next (after
        // bar's activation object) object
        // of the bar's scope chain
        console.log(x + y + z);
      })();
    })();

    在作用域链中,每个AO通过一个隐藏属性__parent__引用下一个对象。

    上述代码的结构如下

    闭包

    在javascript中,函数运行完毕就会释放局部变量。在这样一个情况下,当一个函数返回值也是函数时,外部函数执行完毕,而返回的函数还未执行时,未了让返回的函数可以访问外部函数的变量,就引入了闭包概念。

    在外部函数运行时,定义返回函数,此时,将外部函数的作用域链存储到[[Scope]]属性当中,在返回函数调用时,创建activation object,再将activation object和[[Scope]]属性作为它的作用于链。

    也因此,[[Scope]]属性存在对父函数变量的引用,即使父函数运行完毕,也不会释放变量。

    Scope chain = Activation object + [[Scope]]

    再一次提醒,重点是,当函数定义的时候,存储了父函数的作用域链,这样它就可以在自己执行的时候,寻找父函数中的变量。

    下面巩固一下闭包的工作流程和一个特别的情况。

    function baz() {
      var x = 1;
      return {
        foo: function foo() { return ++x; },
        bar: function bar() { return --x; }
      };
    }
     
    var closures = baz();
     
    console.log(
      closures.foo(), // 2
      closures.bar()  // 1
    );

    当baz运行时,定义了两个函数foo和bar,此时复制了baz的作用域链到两个函数的[[scope]]属性。然后到foo和bar调用时创建自己的activition object,并将activition object与[[scope]]的合并结果作为它们自己的作用域。

    而他们各自的 [[scope]]里的x实际都是指baz AO里的x。当一个函数对x修改时,另一个函数再引用实际是修改后的结果。

  • 相关阅读:
    2020了,初/中级前端面试你应该知道的(上)
    Vue页面权限控制和动态添加路由
    Javascript获取数组中最大和最小值
    localStorage和cookie的跨域解决方案
    移动端常见问题汇总
    码云git本地仓库链接远程仓库
    IntelliJ IDEA Activation code亲测可用
    Sping4之注入参数
    Sping4之依赖注入
    Spring核心之IOC
  • 原文地址:https://www.cnblogs.com/winderby/p/4074632.html
Copyright © 2020-2023  润新知