博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
01-执行上下文与变量对象
阅读量:6591 次
发布时间:2019-06-24

本文共 4629 字,大约阅读时间需要 15 分钟。

执行上下文

  • 可执行代码

    avaScript中可执行代码(executable code)分为三种:全局代码,函数代码和eval代码。 可执行代码与执行上下文的概念是相对的,在某些语义下,可执行代码与执行上下文是等价的。

  • 执行上下文

    执行上下文(Execution Context,EC),也称执行环境,每当控制器执行到可执行代码的时,就会进入到一个执行上下文。 执行上下文可以理解为可执行代码的"运行环境",分为三种:

    全局环境:当一段程序开始执行时,会首先进入全局执行文环境,浏览器中是window对象,只有没有关闭浏览器,一直存在。  函数环境:每当函数被调用执行时,就会进入一个新的上下文环境。(函数递归调用也会进入)  eval环境:eval函数调用的时候产生的执行上下文。复制代码

    执行上下文也可以抽象理解为一个对象,这个对象都有三个属性:

    变量对象(variable object)、作用域链(scope chain)、this指针(this value)。复制代码

    不同执行上下文变量对象略有不同:

    全局上下文中的变量对象就是全局对象,允许通过变量对象的属性名来间接访问。  函数上下文中用活动对象来表示变量对象,通过函数的arguments属性初始化。复制代码

    执行上下文生命周期分为两个阶段:创建阶段和代码执行阶段

    创建阶段:创建变量对象,建立作用域链,确定this指向  代码执行阶段:变量赋值,函数引用,执行其他代码复制代码
  • 执行上下文栈

JavaScript引擎通过执行上下文栈(Execution Context Stack)来管理执行上下文。

当一个执行上下文(caller)激活了另一个上下文(callee),这个caller就会暂停它自身的执行,将控制权交给callee,于是callee被压入栈顶,称为当前上下文。当这个callee的上下文结束之后被弹出,然后caller从暂停的地方继续执行。一个callee可以用返回(return)或抛出异常(exception)来结束自身的上下文。复制代码

ECStack底部永远都是全局上下文(global EC),而顶部就是当前(激活的)执行上下文(active EC)。

// demo1    var scope = "global scope";    function checkscope(){        var scope = "local scope";        function foo(){            return scope;        }        return foo();    }    checkscope();    // 1.首先向执行上下文栈中压入全局执上下文    ECStack = [globalContext];    // 2.调用checkscope(),checkscope执行上下文被压入ECStack    ECStack.push[checkscopeContext] = [        checkscopeContext,        globalContext    ];    // 3.调用foo(),foo执行上下文被压入ECStack    ECStack.push[fooContext] = [        fooContext,        checkscopeContext,        globalContext    ];    // 4.foo()执行结束,foo执行上下文被弹出ECStack    ECStack.pop[fooContext] = [        checkscopeContext,        globalContext    ];    // 5.checkscope()执行结束,checkscope执行上下文被弹出ECStack    ECStack.pop[checkscopeContext] = [globalContext];    // demo2    var scope = "global scope";    function checkscope(){        var scope = "local scope";        function foo(){            return scope;        }        return foo;    }    checkscope()();    // 1.首先向执行上下文栈中压入全局执上下文    ECStack = [globalContext];    // 2.调用checkscope(),checkscope执行上下文被压入ECStack    ECStack.push[checkscopeContext] = [        checkscopeContext,        globalContext        ];    // 3.checkscope()执行结束后返回foo函数体,checkscope执行上下文被弹出ECStack    ECStack.pop[checkscopeContext] = [globalContext];    // 4.foo函数体被返回后调用执行,foo执行上下文被压入ECStack    ECStack.push[fooContext] = [        fooContext,        globalContext    ];    // 5.foo()执行结束,foo执行上下文被弹出ECStack    ECStack.pop[fooContext] = [globalContext];复制代码

变量对象

变量对象(variable object,VO)是与执行上下文相关的数据作用域,用于存储定义在上下文中的变量和函数声明。 函数表达式不包含在变量对象中。

var num = 10; // 变量声明    function fun() {} // 函数声明, FD    (function bar() {}); // 函数表达式, FE    console.log(      this.num === num, // true      window.fun === fun // true    );    console.log(bar); //  "bar" is not defined    // globalContext.VO = {        fun: 
, // 函数fun的引用地址 num: undefined // 变量num,赋值为undefined }复制代码

在全局上下文中,变量对象就是全局对象(在浏览器中是window对象),允许通过全局对象的属性名来访问全局变量。 在函数执行上下文中,用活动对象(active object,AO)来表示变量对象,AO不能直接访问。 活动对象除了变量和函数声明,还存储了形参和arguments对象。 arguments对象是对形参的一个映射,但是值是通过索引来获取(类数组)。

function foo(x, y) {      var z = 30;      function bar() {} // FD      (function baz() {}); // FE    }    foo(10, 20);    //fooContext.AO = {        arguments: {            0: 10,            1: 20,            length: 2            },        bar: 
, x: 10, y: 20, z: 30 }复制代码

在进入执行环境时,变量对象会进行如下初始化:

(1)arguments对象,对象中的值被赋予具体的实参值。(2)函数的形参:创建一个属性,其属性名为形参名,其值为实参的值;对于没有传递的参数,其值为undefined。(3)函数声明:创建一个属性,其属性名和值都是函数对象创建出来的,其值为指向某个函数对象的引用;如果该函数名的属性已存在,则该属性会被新的引用覆盖。(4)变量声明:创建一个属性,其属性名即为变量名,其值为undefined。如果变量名与已声明的形参或函数名相同,则会直接跳过,原属性值不会被修改。变量只能使用var关键字声明,不使用var关键字的赋值语句仅仅是给全局对象创建了一个新属性,不是变量。复制代码

总结:在进入执行上下文的时候(比如进入全局环境或者某个函数被调用),变量对象除了arguments、函数的声明以及参数被赋予了具体的属性值,其它的变量属性默认的都是undefined。在执行到函数内部的具体某个语句的时候,上面所述的值为undefined的变量,其值都会被赋予具体的值。

function test() {        var a = 1;        function foo() {            return 2;        }        var bar = function () {            return 'hello';        };    }    test();    // test()调用时进入该函数执行上下文    testContext = [        VO: {}, // 创建变量对象        Scope: {}, // 建立作用域链        this:{} // 确定this指向    ]    // 创建变量对象VO,里面的属性不能被访问    AO = {        arguments: {length: 0}, // 初始化Arguments对象        foo: 
, // 函数foo的引用地址 a: undefined // 变量a,赋值为undefined bar: undefined // 变量bar,值为undefined } // 执行阶段 AO = { arguments: {length: 0}, foo:
, a: 1 // 变量a,赋值该为1 bar:
// 匿名函数的引用地址赋值给bar }复制代码

每进入到一个执行环境都会创建一个变量对象,这个对象中记录了在当前执行环境中可以访问到的变量和函数,它们以变量对象的属性形式存在。也就是说这个变量对象成为“作用域”这个抽象概念的实体。

参考资料:

转载于:https://juejin.im/post/5a9e5b9ef265da239235dc9f

你可能感兴趣的文章
第18天:京东网页头部制作
查看>>
好消息:Dubbo & Spring Boot要来了
查看>>
面向对象封装的web服务器
查看>>
南开大学提出新物体分割评价指标,相比经典指标错误率降低 69.23%
查看>>
初创公司MindMaze研发情绪反应VR,让VR关怀你的喜怒哀乐
查看>>
绕开“陷阱“,阿里专家带你深入理解C++对象模型的特殊之处
查看>>
ElasticSearch
查看>>
9-51单片机ESP8266学习-AT指令(测试TCP服务器--51单片机程序配置8266,C#TCP客户端发信息给单片机控制小灯的亮灭)...
查看>>
香港设计师带来仿生机器人,其身体 70% 构造均由3D打印完成
查看>>
不规则物体形状匹配综述
查看>>
自动化设计-框架介绍 TestCase
查看>>
CJ看showgirl已经out!VR体验才是王道
查看>>
postgresql 数组类型
查看>>
Vue+Webpack常见问题(持续更新)
查看>>
栈与递归的实现
查看>>
Manually Summarizing EIGRP Routes
查看>>
spring boot 1.5.4 整合webService(十五)
查看>>
modsecurity(尚不完善)
查看>>
获取.propertys文件获取文件内容
查看>>
Redis3.0.5配置文件详解
查看>>