• JavaScript作用域学习笔记


    文章部分实例和内容来自鸟哥的blogJavascript作用域原理

    前端基础进阶(三):变量对象详解    波同学 

    在JS中,作用域的概念和其他语言差不多,是JS中一个极为重要的概念。在每次调用一个函数的时候 ,就会进入一个函数内的作用域,当从函数返回以后,就返回调用前的作用域.

    理解作用域,首先理解几个概念

    变量对象:执行环境(execution context)定义所有的变量和函数都存在这个对象中。虽然我们编写的代码无法访问这个对象,但是解析器在处理数据时后台会使用它

    var foo=10;
        function func(){};
        
        //因为是在全局作用域当中,so...
        Global VO={
            foo:10,
            func:<function>
        }
    

     变量对象的创建经历以下几个过程:

    1. 建立arguments对象,检查上下文中的参数,建立该对象下的属性和属性值。
    2. 检查当前上下文的函数声明
    3. 检查上下文变量声明,每找到一个变量声明,就在变量对象中以变量名建立一个属性,属性值为undefined。如果该变量名的属性已经存在,为了防止同名的函数被修改为undefined,则会直接跳过,原属性值不会被修改。

    由上可以看出function的优先级比var优先级高

    活动对象:在调用func的时候, 会创建一个活动对象(假设为aObj, 由JS引擎预编译时刻创建, 后面会介绍),并创建arguments属性

    function foo(x,y){
            var z=30;
            function bar(){};
        }
        foo(10,20);
        //当执行到foo(10,20)时即会产生AO
        Activation Object={
            z:30,
            x:10,
            y:20,
            bar:<function>,
            arguments:{0:10,1:20,length:2}
        }
    

     

     注意到, 因为函数对象的[[scope]]属性是在定义一个函数的时候决定的, 而非调用的时候, 所以如下面的例子

    var name = 'laruence';
         function echo() {
              alert(name);
         }
     
         function env() {
              var name = 'eve';
              echo();
         }
     
         env();
    

      结果

    laruence
    

     可以看出一段函数被激活时有两个阶段,一个是创建阶段,一种执行阶段

                           1.生成变量对象                                 1.变量赋值

    创建阶段 ==》2.建立作用域        =====》执行==》2.函数引用  ===》出栈 回收

                           3.确定this的指向                                3.执行代码

    看下面梨字

    // demo01
    function test() {
        console.log(a);
        console.log(foo());
     
        var a = 1;
        function foo() {
            return 2;
        }
    }
     
    test();
    

      从上下文开始理解,全局作用域的运行test()时,test()的执行上下文开始创建。

    创建过程
    testEC = {
        // 变量对象
        VO: {},
        scopeChain: {},
        this: {}
    }
     
    // 因为本文暂时不详细解释作用域链和this,所以把变量对象专门提出来说明
     
    // VO 为 Variable Object的缩写,即变量对象
    VO = {
        arguments: {...},  //注:在浏览器的展示中,函数的参数可能并不是放在arguments对象中,这里为了方便理解,我做了这样的处理
        foo: <foo reference>  // 表示foo的地址引用
        a: undefined
    }
    

      变量对象和活动对象是一种对象,只不过是处于执行上下文的不同生命周期。

    // 执行阶段
    VO ->  AO   // Active Object
    AO = {
        arguments: {...},
        foo: <foo reference>,
        a: 1
    }
    

      因此demo1变成了如下代码

    function test() {
        function foo() {
            return 2;
        }
        var a;
        console.log(a);
        console.log(foo());
        a = 1;
    }
     
    test();
    

      实际的例子:

    function factory(){
        var name="laruence";
        var intro=function(){
            console.log("I'm "+name);
        }
        return intro;
    }
    function app(para){
        var name=para;
        var func=factory();
        func();
    }
    
    app("eve");
    

      当调用app的时候, scope chain是由: {window活动对象(全局)}->{app的活动对象} 组成.

    此时的scope chain如下:

    [[scope chain]]=[
    Active Object={
        this:window,
        arguments:{0:"eve",length:1}
        name:'eve'
        func:<function>
        para:"eve",
    },
    Global VO={
        this:window,
        app:<function>,
        window:<object>,
        document:<object>
    }
    ]
    

      

      当调用进入factory的函数体的时候, 此时的factory的scope chain为:

    [[scope chain]]={
    Active Object={
        this:window,
        arguments:{},
        name:"laruence",
        intro:<function>,
    },
    Global Object(Variable Object)={
       this:window,
       factory:<function>,
       app:<function>,
       window:<object>,
       document:<object>
    }
    }
    

      在定义intro函数的时候,intro函数[[scope]]为:

    [[scope chain]]={
    Object={
        name:"laruence",
        intro:<function>,
        this:<factory>,     //注意这里的this指向
        arguments:{}
    },
    Gloabal Object={
        this:window,
        factory:<function>,
        document:<object>,
        window:<object>
    }
    }
    

      factory函数返回后,在app体内调用intro时,发生了标识符的解析,而此时的scope chain是:

    [[scope chain]]={
      intro AO={
            <intro 活动对象>
      } ,
      Factory AO={
            name:"laruence",
            intro:<function>,
      },
      Global VO={
            this:window,
            factory:<function>,
            document:<obeject>,
            window:<object>
      }
    }
    

      因为scope chain中,并不包含factory活动对象. 所以, name标识符解析的结果应该是factory活动对象中的name属性, 也就是’laruence’.

  • 相关阅读:
    C#中常见的系统内置委托用法详解(抄录)
    ClassifyHandler 分类处理结构
    AutoFac Ioc依赖注入容器
    深入理解DIP、IoC、DI以及IoC容器
    ASP.NET MVC的请求处理流程
    电商秒杀功能实现
    MVC之Global.asax解析
    MVC基类控制器的会话丢失重新登录权限过滤
    MVC的Action上下文:ActionExecutingContext
    ASP.NET与MVC架构区别总结
  • 原文地址:https://www.cnblogs.com/meng2017/p/8167623.html
Copyright © 2020-2023  润新知