likes
comments
collection
share

执行上下文 + 执行上下文栈【JS深入知识汇点1】

作者站长头像
站长
· 阅读数 0

执行上下文

What's EC(执行上下文) ?

每次当控制器转到 ES 可执行代码可执行代码类型就三种:全局代码、函数代码、eval代码)的时候,就会进入到一个执行上下文

EC 建立的细节

  1. 创建EC阶段(时间节点:函数被调用,但未执行任何内部代码时)
    • 初始化 Scope Chain(作用域链)
    • 创建变量对象
    • 求 this 的值
  2. 执行阶段:在当前上下文上运行函数代码,并随着代码一行行执行指派变量的值(AO)

EC 抽象为对象

ECObject : {
    scopeChain: {},
    variableObject: {},
    this: {}
}

What's VO(变量对象) ?

每一个执行上下文都会分配一个变量对象,它保存了执行上下文中可被访问但不能被 delete 的函数标示符、形参、变量声明等.

VO 创建的细节:

  1. 创建 arguments 对象,检查上下文,初始化参数名称和值,并创建引用的复制。
  2. 扫描上下文的函数声明(非函数表达式)
    • 为发现的每一个函数,在 VO 上创建一个属性,属性名就是变量的名字,初始化为此函数在内存中的引用
    • 如果函数的名字已存在,引用指针被重写(包括形参)
  3. 扫描上下文的变量
    • 为发现的每一个变量,在 VO 上创建一个属性,属性名就是变量的名字,初始化为 undefined
    • 如果函数名字已存在,则不会进行任何操作继续扫描

What's AO(活动对象) ?

当函数被激活,那么一个 AO 就会被创建并且分配给 EC,就是激活的 VO,当进入一个执行上下文中,这个执行上下文的 VO 就会被激活,变成 AO,只有 AO 的各种属性才可以被访问。

EC 创建抽象为代码

function(i) {
    var a = 'hello';
    var b = function() {}
    function c() {}
}
foo(22);
// 1.创建 EC 结果
ECObject = {
    scopeChain: {...},
    variableObject : {
        arguments: {
            0: 22,
            length: 1
        },
        i: 22, // 重点
        c: pointer to functionc c (), //引用
        a: undefined,
        b: undefined
    },
    this: {...}
}
// 2.执行代码完成后的 EC 
ECObject = {
    scopeChain: {...},
    variableObject: {
        arguments: {
            0: 22,
            length: 1
        },
        i: 22,
        c: pointer to functionc c (), 
        a: 'hello',
        b: pointer to function() {}
    }
}

执行上下文栈

JS 是单线程语言,也就是同个时段只能做一件任务,完成之后才可以继续下一个任务。在一个 JS 程序中,会产生多个 EC,JS 引擎会以 栈 的方式来处理这些 EC。

What's ECS (执行上下文栈) ?

ECS,具有 LIFO(后进先出)的数据结构

执行上下文 +  执行上下文栈【JS深入知识汇点1】

ECS 运行细节

1.第一次遇到JS程序时,会创建一个 全局的 EC ,用 globalContext 表示它,并且压入 ECS 2.当执行一个函数时,会创建一个EC,并且压入 ECS,当函数处理完毕的时候,会把函数的 EC 从栈中弹出。

ECS 运行抽象为代码

function fun3() {
    console.log('fun3')
}
function fun2() {
    fun3()
}
function fun1() {
    fun2()
}
fun1();
// 伪代码
ECStack = [];
ECStack.push(golbalContext);
ECStack.push(<fun1> functionContext)
ECStack.push(<fun2> functionContext)
ECStack.push(<fun3> functionContext)
ECStack.pop();
ECStack.pop();
ECStack.pop();

难题解析

前面的知识点学习后,现在就到了检验学习成果的时候啦

Q1:代码A和B的结果一样,但究竟有什么不同呢?

// 代码A
var scope = "global scope";
function checkscope(){
    var scope = "local scope";
    function f(){
        return scope;
    }
    return f();
}
checkscope();
// 代码B
var scope = "global scope";
function checkscope(){
    var scope = "local scope";
    function f(){
        return scope;
    }
    return f;
}
checkscope()();

其实就是 ECS 的变化不一样

// 代码A
ECStackA = [globalContext];
ECStackA.push(<checkscope> functionContext)
ECStackA.push(<f> functionContext)
ECStack.pop()
ECStack.pop()

//代码B
ECStackB = [globalContext]
ECStackB.push(<checkscope> functionContext)
ECStackB.pop()
ECStackB.push(<f> functionContext)
ECStackB.pop()

Q2:写出如下代码的打印结果

// 在函数传值时,是把 webSite 的引用地址 copy 给 函数
function changeObjProperty(o) {
  // 改变对应地址内的对象属性值
  o.siteUrl = "http://www.baidu.com"
  变量 o 指向新地址,以后的变动和旧地址无关
  o = new Object()
  o.siteUrl = "http://www.google.com"
} 
let webSite = new Object();
changeObjProperty(webSite);
console.log(webSite.siteUrl); // "http://www.baidu.com"