脚本宝典收集整理的这篇文章主要介绍了JavaScript的执行过程(深入执行上下文、GO、AO、VO和VE等概念),脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。
编写一段JavaScript代码,它是如何执行的呢?简单来说,JS引擎在执行JavaScript代码的过程中需要先解析再执行。那么在解析阶段JS引擎又会进行哪些操作,接下来就一起来了解一下JavaScript在执行过程中的详细过程,包括执行上下文、GO、AO、VO和VE等概念的理解。
首先,JS引擎会在执行代码之前,也就是解析代码时,会在我们的堆内存创建一个全局对象:Global Object(简称GO),观察以下代码,在全局中定义了几个变量:
示例代码:
var name = 'curry'
var message = 'I am a coder'
var num = 30
JS引擎内部在解析以上代码时,会创建一个全局对象(伪代码如下):
var GlobalObject = {
Math: '类',
Date: '类',
String: '类',
setTimeout: '函数',
setInterval: '函数',
window: GlobalObject,
...
name: undefined,
message: undefined,
num: undefined
}
了解了什么是全局对象后,下面就来聊聊代码具体执行的地方。JS引擎为了执行代码,引擎内部会有一个执行上下文栈(Execution Context Stack,简称ECS),它是用来执行代码的调用栈。
(1)ECS如何执行?先执行谁呢?
(2)那么全局执行上下文(GEC)包含那些内容呢?
下面就通过一幅图,来看看GEC被放入ECS后的表现形式:
接下来,将全局代码复杂化一点,再来看看调用栈调用全局执行上下文(GEC)的过程。
实例代码:
var name = 'curry'
console.log(message)
var message = 'I am a coder'
function foo() {
var name = 'foo'
console.log(name)
}
var num1 = 30
var num2 = 20
var result = num1 + num2
foo()
调用栈调用过程:
1.初始化全局对象。
2.构建一个全局执行上下文(GEC),代码执行前将VO的内存地址指向GlobalObject(GO)。
3.将全局执行上下文(GEC)放入执行上下文栈(ECS)中。
4.从上往下开始执行全局代码,依次对GO对象中的全局变量进行赋值。
var name = 'curry'
时,就从VO(对应的就是GO)中找到name属性赋值为curry;console.log(message)
,就从VO中找到message,注意此时的message还为undefined,因为message真正赋值在下一行代码,所以就直接打印undefined(也就是我们经常说的变量作用域提升);var result = num1 + num2
,也是从VO中找到num1和num2两个属性的值进行相加,然后赋值给result,result最终就为50;foo()
,也就是需要去执行foo函数了,这里的操作是比较特殊的,涉及到函数执行上下文,下面来详细了解;在执行全局代码遇到函数如何执行呢?
继续来看上面的代码执行,当执行到foo()
时:
console.log(name)
时,就会去foo的VO(对应的就是foo函数的AO)中找到name属性值并打印;上文中提到了很多次VO,那么VO到底是什么呢?下面从ECMA新旧版本规范中来谈谈VO。
在早期ECMA的版本规范中:每一个执行上下文会被关联到一个变量环境(Variable Object,简称VO),在源代码中的变量和函数声明会被作为属性添加到VO中。对应函数来说,参数也会被添加到VO中。
在最新ECMA的版本规范中:每一个执行上下文会关联到一个变量环境(Variable Environment,简称VE),在执行代码中变量和函数的声明会作为环境记录(Environment Record)添加到变量环境中。对于函数来说,参数也会被作为环境记录添加到变量环境中。
了解了上面相关的概念和调用流程之后,就来看一下存在函数嵌套调用的代码是如何执行的,以及执行过程中的一些细节,以下面代码为例:
var message = 'global'
function foo(m) {
var message = 'foo'
console.log(m)
function bar() {
console.log(message)
}
bar()
}
foo(30)
初始化全局对象(GO),执行全局代码前创建GEC,并将GO关联到VO,然后将GEC加入ECS中:
开始执行全局代码,从上往下依次给全局属性赋值:
执行到foo函数调用,准备执行foo函数前,创建foo函数的AO:
创建foo函数的FEC,并加入到ECS中,然后开始执行foo函数体内的代码:
执行到bar函数调用,准备执行bar函数前,创建bar函数的AO:
创建bar函数的FEC,并加入到ECS中,然后开始执行bar函数体内的代码:
console.log(message)
,会先去bar函数自己的VO中找message,没有找到就往上层作用域的VO中找;全局中所有代码执行完成,bar函数执行上下文出栈,foo函数AO对象失去了引用,进行销毁。
接着foo函数执行上下文出栈,foo函数AO对象失去了引用,进行销毁,同样,foo函数AO对象销毁后,bar函数的存储空间也失去引用,进行销毁。
函数在执行前就已经确定了其父级作用域,与函数在哪执行没有关系,以函数声明的位置为主;
执行代码查找变量属性时,会沿着作用域链一层层往上查找(沿着VO往上找),如果一直找到全局对象中还没有该变量属性,就会报错未定义;
上文中提到了很多概念名词,下面来总结一下:
名词 | 解释 |
---|---|
ECS | 执行上下文栈(Execution Context Stack),也可称为调用栈,以栈的形式调用创建的执行上下文 |
GEC | 全局执行上下文(Global Execution Context),在执行全局代码前创建 |
FEC | 函数执行上下文(Functional Execution Context),在执行函数前创建 |
VO | Variable Object,早期ECMA规范中的变量环境,对应Object |
VE | Variable Environment,最新ECMA规范中的变量环境,对应环境记录 |
GO | 全局对象(Global Object),解析全局代码时创建,GEC中关联的VO就是GO |
AO | 函数对象(Activation Object),解析函数体代码时创建,FEC中关联的VO就是AO |
以上是脚本宝典为你收集整理的JavaScript的执行过程(深入执行上下文、GO、AO、VO和VE等概念)全部内容,希望文章能够帮你解决JavaScript的执行过程(深入执行上下文、GO、AO、VO和VE等概念)所遇到的问题。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。