【JavaScript由浅入深】执行上下文
在了解这些之前我们先来看看JavaScript语言的执行过程,大致分为下面几个步骤:
- 语法检查:对整体代码进行词法分析,语法分析
- 代码预解析:生成GO、AO对象(也称作预编译)
- 解释执行:逐行运行代码
一、执行上下文
1.1 执行上下文类型
(1)全局执行上下文
任何不在函数内部的都是全局执行上下文,它首先会创建一个全局的window对象,并且设置this的值等于这个全局对象,一个程序中只有一个全局执行上下文。
(2)函数执行上下文
当一个函数被调用时,就会为该函数创建一个新的执行上下文,函数的上下文可以有任意多个。
(3)eval
函数执行上下文
执行在eval函数中的代码会有属于他自己的执行上下文,不过eval函数不常使用。
1.2 执行上下文栈
- js引擎内部有一个执行上下文栈(Execution Context Stack,简称ECS),它是用于执行代码的调用栈。
- 当JavaScript执行代码时,首先遇到全局代码,会创建一个全局执行上下文并且压入执行栈中
- 每当遇到一个函数调用,就会为该函数创建一个新的执行上下文并压入栈顶
- 引擎会执行位于执行上下文栈顶的函数
- 当函数执行完成之后,执行上下文从栈中弹出,继续执行下一个上下文
- 当所有的代码都执行完毕之后,从栈中弹出全局执行上下文
1.3 VO对象
每一个执行上下文会关联一个VO(Variable Object,变量对象),变量和函数声明会被添加到这个VO对象中
简单来说执行上下文就是指:
在执行一点JS代码之前,需要先解析代码。解析的时候会先创建一个全局执行上下文环境,先把代码中即将执行的变量、函数声明都拿出来,变量先赋值为undefined,函数先声明好可使用。这一步执行完了,才开始正式的执行程序。
在一个函数执行之前,也会创建一个函数执行上下文环境,跟全局执行上下文类似,不过函数执行上下文会多出this、arguments和函数的参数。
- 全局上下文:变量定义,函数声明
- 函数上下文:变量定义,函数声明,
this
,arguments
二、AO与GO
- AO和 GO,说白了就是局部变量对象和全局变量对象。
- AO(Activation Object ):其包含了函数执行期的上下文内容。
- GO(Clobal Object)):其包含全局执行的上下文内容。
- AO是一个即时的存储容器,函数执行完毕以后,AO是要销毁的。
三、全局代码执行过程
-
js引擎会在执行代码之前,会在堆内存中创建一个全局对象:Global Object(GO)
- 该对象 所有的作用域(scope)都可以访问;
- 里面会包含Date、Array、String、Number、setTimeout、setInterval等等;
- 其中还有一个window属性指向自己;
-
执行上下文栈执行全局的代码块
- 全局的代码块为了执行会构建一个全局执行上下文 Global Execution Context(GEC);
- GEC会 被放入到执行上下文栈(ECS)中 执行;
-
GEC被放入到ECS中里面包含两部分内容:
- 第一部分:在代码执行前,在parser转成AST的过程中,会将全局定义的变量、函数等加入到GlobalObject中,但是并不会 赋值;
- 这个过程也称之为变量的作用域提升(hoisting)
- 第二部分:在代码执行中,对变量赋值,或者执行其他的函数
-
当全局代码被执行的时候,VO就是GO对象
以下代码的全局代码执行过程如下:
var message = "Global Message"
function fn() {
var message = "foo Message"
}
var num1 = 10
var num2 = 20
var result = num1 + num2
foo()
执行前:
执行后:
四、函数执行代码过程
- 在执行的过程中执行到一个函数时,就会根据函数体创建一个函数执行上下文(Functional Execution Context,简称FEC), 并且压入到执行上下文栈中。
- 因为每个执行上下文都会关联一个VO,那么函数执行上下文关联的VO是什么呢?
- 当进入一个函数执行上下文时,会创建一个AO对象(Activation Object);
- 这个AO对象会使用arguments作为初始化,并且初始值是传入的参数;
- 这个AO对象会作为执行上下文的VO来存放变量的初始化;
以下代码的函数代码执行过程如下:
var message = "Global Message"
function fn(num) {
var message = "foo Message"
var age = 18
var height = 1.88
console.log("foo function");
}
foo(123)
var num1 = 10
var num2 = 20
var result = num1 + num2
执行前:
执行后: