• JavaScript执行环境


      执行环境(Execution Context,也称为"执行上下文")是JavaScript中最为重要的一个概念。执行环境定义了变量或函数有权访问的其它数据,决定了各自的行为。当JavaScript代码执行的时候,会进入不同的执行环境,这些不同的执行环境就构成了执行环境栈。

      JavaScript中主要存在三种执行环境:

    •  全局执行环境

       JavaScript代码执行的默认环境。通常被默认为window对象,所有的全局变量和函数都作为window对象的属性和方法存在。当执行环境中的代码执行完毕之后,执行环境被销毁,其中的所有变量和函数也随之销毁。对于全局执行环境来说,当关闭网页或浏览器时,该环境被销毁。

    •  函数执行环境

         当执行一个JavaScript函数时,函数的环境被推入环境栈中,执行完毕之后,栈将执行环境推出,将控制权转交给之前的执行环境。

    •  Eval环境

       执行eval()函数时创建。

      对于执行环境栈,请看如下代码:

    var a = "global";
    function example(){
         console.log(a);   
    }
    
    function outer(){
         var b = "outer";
         console.log(b);   
            
         function inner(){
              var c = "inner";
              console.log(c);
              example();
         }
         
         inner();
    }
    
    outer();
    

      代码首先进入全局执行环境,然后依次进入outer,inner和example的执行环境,执行环境栈可以表示为:

      

       每个执行环境都有三个重要的属性,变量对象(VO)、作用域链(scope chain)和this。下面首先看一下变量对象。

      变量对象和活动对象(VO和AO)

       变量对象

      每个执行环境都有一个与之关联的变量对象(variable object),环境中定义的所有变量和函数都保存在这个对象中。当代码在一个环境中执行时,会创建当前变量对象的一个作用域链(scope chain)。作用域链的最前端,始终是当前执行环境的变量对象。如果执行环境是函数,则其活动对象(activation object)作为变量对象。作用域链的下一个变量对象来自于父执行环境,而再下一个变量对象来自于父环境的外部环境,以此类推构成完整的作用域链,而最外层的变量对象始终是全局执行环境的变量对象。

      一般来说,变量对象(VO)中包含以下信息:

    • 变量
    • 函数声明
    • 函数的形参

      当JavaScript代码执行的时候,如果试图寻找一个变量或函数,就会首先寻找VO。对于前面提到的代码,全局执行环境的VO如下所示:

      

      对于VO来说,函数表达式不包含在VO中,没有使用var声明的变量也不包含在VO中,这种方式只是给Global添加了一个属性。

      活动对象

      只有全局执行环境的变量对象允许通过VO的属性名称间接访问。但在函数执行环境中,VO是不允许被直接访问的。此时,由活动对象(Activation Object,简称AO)扮演VO的角色。活动对象在进入函数执行环境时被创建,它通过函数的arguments属性初始化,其中Arguments Objects是函数执行环境中活动对象AO的内部对象。

      VO和AO的关系,简单点说就是,VO在不同的执行环境中有不同的变现形式。在全局执行环境中,可以直接使用VO;但是在函数执行环境中,AO被创建。

      在上面的代码例子中,当开始执行outer函数的时候,outer函数的AO被创建如下图所示:

      

      执行环境的具体过程

      当进入一个执行环境的时候,JavaScript解释器会创建新的执行环境,但具体是怎么做的呢?主要分为两个阶段:

    • 创建阶段
      • 创建作用域链
      • 创建VO/AO
      • 设置this的值
    • 执行阶段
      • 设置变量的值
      • 设置函数的引用
      • 解释执行代码

      对于"创建VO/AO"这一步,JavaScript解释器主要做了下面的事情:

    • 根据函数参数,创建并初始化arguments object
    • 根据函数内部代码查找函数声明
      • 对于找到的所有函数声明,将函数名和引用全部存入VO/AO
      • 如果存在同名函数,进行覆盖
    • 根据函数内部代码查找变量声明
      • 对于找到的所有变量声明,全部存入VO/AO,并初始化为"undefined"
      • 如果变量名称和已经声明的形参或函数相同,那么变量声明不会干扰这类属性

      看下面的例子:

    function example(p) {
        var a = 'hello';
        var b = function b() {...};
        function c() {...}
    }
    
    example(2);
    

       对于上面的例子,在执行环境创建阶段,会得到如下的执行环境对象:

    exampleExecutionContext={
         scopeChain: {...},
         VO:{
              arguments:{
                    0:2,
                    length:1
              }
              p:2,
              c:pointer to function c()   
              a:undefined,
              b:undefined
         },
      
         this: {...}
    }   
    

       在代码执行阶段,环境对象会被更新,如下所示:

    exampleExecutionContext={
         scopeChain: {...},
         VO:{
              arguments:{
                    0:2,
                    length:1
              }
              p:2,
              c:pointer to function c()   
              a:'hello',
              b:pointer to function b()
         },
      
         this: {...}
    }  
    
  • 相关阅读:
    静态字段==全局变量
    异常处理原则
    结合冒泡排序学习委托和事件系列一
    Linq详解
    linq的延迟执行和立即执行
    统计一个目录下所有.cs文件的行数
    Multicast委托和Observer模式——委托系列二
    usb设备插入电脑,电脑显示MTP带有黄色感叹号,(Windows已找到设备的去驱动程序,但在尝试安装他们时遇到错误),解决办法
    博客地址变更
    Flex在线阅读 FlexBook FlexPaper
  • 原文地址:https://www.cnblogs.com/neusc/p/5771150.html
Copyright © 2020-2023  润新知