执行上下文 + 执行上下文栈【JS深入知识汇点1】
执行上下文
What's EC(执行上下文) ?
每次当控制器转到 ES 可执行代码(可执行代码类型就三种:全局代码、函数代码、eval代码)的时候,就会进入到一个执行上下文。
EC 建立的细节
- 创建EC阶段(时间节点:函数被调用,但未执行任何内部代码时)
- 初始化 Scope Chain(作用域链)
- 创建变量对象
- 求 this 的值
- 执行阶段:在当前上下文上运行函数代码,并随着代码一行行执行指派变量的值(AO)
EC 抽象为对象
ECObject : {
scopeChain: {},
variableObject: {},
this: {}
}
What's VO(变量对象) ?
每一个执行上下文都会分配一个变量对象,它保存了执行上下文中可被访问但不能被 delete 的函数标示符、形参、变量声明等.
VO 创建的细节:
- 创建 arguments 对象,检查上下文,初始化参数名称和值,并创建引用的复制。
- 扫描上下文的函数声明(非函数表达式)
- 为发现的每一个函数,在 VO 上创建一个属性,属性名就是变量的名字,初始化为此函数在内存中的引用
- 如果函数的名字已存在,引用指针被重写(包括形参)
- 扫描上下文的变量
- 为发现的每一个变量,在 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(后进先出)的数据结构

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",
转载自:https://juejin.cn/post/6844904128225738759