一、理解
执行上下文,就是代码被解析和执行时所在环境的抽象概念。JavaScript 中运行任何代码都是在执行上下文中运行。
由js 引擎自动创建的对象,包含对应作用雨中的所有变量属性。
二、执行上下文的类型
- 全局执行上下文: 默认的最基础的执行上下文。不在任何函数中的代码都处于全局执行上下文中。主要完成:(1)创建一个全局对象,在浏览器中全局对象就死window对象。(2)将this指针指向这个全局对象。
- 函数执行上下文:函数调用时都会为函数创建一个新的执行上下文,只有在函数被调用时才会创建,每个函数都有自己的执行上下文。一个程序中可以有任意数量的函数执行上下文。
- Eval函数执行上下文:运行在 eval函数中的代码的自己的指向上下文。
三、执行上下文中重要的是三个部分
- 变量对象(VO,Variable object),
- 作用域链(Scope chain)
- this指向
1、 变量对象:
变量对象是与执行上下文相关的数据作用域,存储了在上下文中定义的变量和函数声明。
全局上下文中的变量对象是全局对象。
(1)全局对象是预定义对象,是JS全局函数和全局属性的占位符。通过使用全局对象,可以访问所有预定义对的对象、属性和函数。(2)可以使用this关键字引用全局对象。全局对象作用与作用域链最低端,意味着所有非限定性的变量和函数的名字都可以为全局对象的属性查找。在浏览器中全局对象是window,在node中全局对象是global。(3)由于全局对象在作用域的最低端,所以顶层JS代码中声明的变量都将成为全局对象的属性。(4)全局对象是Object构造函数的实例,Object.prototype(原型)上预定的属性和方法,可以通过全局对象访问到。
函数上下文中的变量对象用活动对象(activation object,AO)表示。
活动对象在进入函数上下文时被创建的,具有函数的arguments属性作为初始化属性,arguments属性的值就是Arguments对象。
函数上下文的执行过程:(1)预编译(2)执行
function foo(a){
var b = 2;
function c(){}
var d = function(){};
b = 3;
}
foo(1);
(1)预编译:
- 创建AO对象,寻找形参和变量声明
- 把形参和变量名作为AO对象的属性名,值为undefind
- 把实参赋值给形参,实参形参相统一
- 寻找函数声明,值为函数体。
AO = {
arguments:Arguments对象
a:1,
d:undefind,
b:undefind.
c:function c
}
(2)执行:
顺序执行代码,修改变量的值。
function foo(a){
var b = 2;
function c(){}
var d = function(){};
b = 3;
}
foo(1);
AO = {
arguments:{
0:1,
length:1
},
a:1,
b:3,
c:reference to function c(){}
d:reference to FunctionExpression "d"
}
总结:
1、全局上下文的变量对象初始化是全局对象
2、函数上下文文的变量对象初始化质保函Argunments对象
3、在进入执行上下文时会给变量对象添加形参、变量声明、函数声明等初始的属性值(预编译)
4、在变量执行阶段,会修改变量对象的属性值。
2、作用域链
在深入JS变量对象中,查找变量时,js引擎会从当前的上下文的变量对象中查找,如果没有找到,就会从父级执行上下文的变量对象中查找,一直找到全局上下文的变量对象,也就是全局对象。这种多个执行上下文构成的链表就叫做作用域链。
函数的作用域在函数定义的时候就决定了,这是因为函数内部有一个[[scope]],当函数创建的时候,它会保存所有父变量对象到其中,可以理解为[[scope]]中就是所有父变量对象的作用域链,但是[[scope]]并不代表完整的作用域链。
function foo(){
function bar(){
}
}
foo.[[scope]]=[
globalContext.V0
];
bar.[[scope]]=[
fooContext.A0
globalfontext.Vo
3、this指向
在全局执行上下文中,this 的值指向全局对象,在浏览器中,this 的值指向 window 对象。
在函数执行上下文中,this 的值取决于函数的调用方式。如果它被一个对象引用调用,那么 this 的值被设置为该对象,否则 this 的值被设置为全局对象或 undefined(严格模式下)
四、执行栈
执行栈,在其他编程语言中也被叫做调用栈,具有 LIFO(后进先出)结构,用于存储在代码执行期间创建的所有执行上下文。
当 JavaScript 引擎首次读取你的脚本时,它会创建一个全局执行上下文并将其推入当前的执行栈。
每当发生一个函数调用,引擎都会为该函数创建一个新的执行上下文并将其推到当前执行栈的顶端。
引擎会运行执行上下文在执行栈顶端的函数,当此函数运行完成后,其对应的执行上下文将会从执行栈中弹出,
上下文控制权将移到当前执行栈的下一个执行上下文。
五、声明周期
执行上下文的生命周期包括三个阶段:创建阶段→执行阶段→回收阶段。
全局 : 准备执行全局代码前产生, 当页面刷新/关闭页面时死亡
函数 : 调用函数时产生, 函数执行完时死亡
六、与作用域的区别
JavaScript 属于解释型语言,JavaScript 的执行分为:解释和执行两个阶段,这两个阶段所做的事并不一样:解释阶段:词法分析,语法分析,作用域规则确定。执行阶段:创建执行上下文,执行函数代码,垃圾回收。
他们最大的区别是:
作用域在解释阶段确定的,并且不会改变;
执行上下文在运行阶段确定的,随时可能改变。
参考:
https://blog.csdn.net/kelly0721/article/details/127026923
https://blog.csdn.net/ksjdbdh/article/details/120111962
https://juejin.cn/post/6844903704466833421