JS高级 - JS执行过程
初始化全局对象
js引擎会在执行代码之前,会在堆内存中创建一个全局对象:Global Object(GO)
- 对象 所有的作用域(scope)都可以访问
- 该对象中存在全局的属性和方法,如Date、Array、String、Number、setTimeout、setInterval等
- 这个GO对象其实就是globalThis对象
- 在globalThis中有globalThis属性,指向自己
执行上下文
js引擎内部有一个执行上下文栈(Execution Context Stack,简称ECS),它是用于执行代码的调用栈
任意一段代码在被执行之前都会创建对应的执行上下文(Execution Contexts), 本被压入ECS后进行执行,执行完成后再出栈
所以在ECS中,栈顶的那个EC一定是正在被执行的那个活跃执行上下文
同样,全局的代码块在执行的时候会构建一个 Global Execution Context(GEC) 被压入ECS中
GEC往往都是栈底的EC,因为它是被最早创建的,也是最早被压入ECS的,
因此当GEC出栈的时候,就意味着JS代码执行完毕
对于任意一个EC,其内部都会初始化三个属性
- 作用域链(scope chain)
- VO (Variable Object)
- this
在GEC中VO对应的就是GO对象,所以使用var声明的变量最终会被挂载到globalThis上
全局代码在被执行的时候,一定会创建GEC,所以全局存在this,且其值为globalThis对象
所以箭头函数在按照作用域链查找this值的时候,最后一定会指向globalThis
通过JS代码创建对应EC是在parser阶段进行的,也就是在转换成对应的AST树的时候完成
在此过程中,会将定义的变量、函数等加入到VO中
- 函数是最先被解析的,在此阶段对应的函数对象会被创建,且VO中存在与函数名同名变量指向该函数对象
- 函数被解析后,会提前收集到所有的变量定义,并将他们挂载到VO对象上,对应的属性值皆被初始化为undefined
- 这个过程被称之为变量的作用域提升(hoisting) 或被称之为预解析
var foo = 'foo'
function foo() {
console.log('function foo')
}
// 函数的预解析是早于变量的预解析
// 所以在本例中,foo变量最早指向的是foo函数对象, 随后又被赋值为了字符串foo
// 因此输出字符串foo
console.log(foo) // => foo
此时,在代码真正被执行前,其内存图类似如下:
EC创建被初始化完成后,才会真正逐行执行对应的JS代码
函数的执行
在执行的过程中执行到一个函数时,就会根据函数体创建一个函数执行上下文(Functional Execution Context,简称FEC),并且压入到EC Stack中
因为每一个EC都需要一个VO对象,用于进行变量和函数的初始化
所以当进入一个函数执行上下文时,会创建一个AO对象(Activation Object)
这个AO对象会使用arguments作为初始化,并且初始值是传入的参数
这个AO对象会作为执行上下文的VO来存放变量的初始化,包括函数内部定义的变量和函数,也包裹定义的形参也会挂载到AO对象上
作用域链
当进入到一个执行上下文时,执行上下文也会关联一个作用域链(Scope Chain)
-
作用域链是一个对象列表,用于变量标识符的求值,也就是用于变量标识符值的查找
-
当进入一个执行上下文时,这个作用域链被创建,并且根据代码类型(这里的类型指的是全局作用域或函数作用域等),添加一系列的对象
-
和this不同,作用域是在函数被定义的那一刻就已经被确定下来,保存在函数内部一个私有属性
[[scopes]]
中也就是说JS的作用域是词法作用域,而不是动态作用域
-
[[scopes]]
属性本质是一个伪数组对象 -
对于全局代码,其作用域链中只有一个值,也就是GO
-
对于函数执行上下文,其EC中对应的scope chain会使用函数对象的
[[scopes]]
属性 -
在查找变量的时候,会优先在VO中进行查找,如果没有会去scope chain中依次进行查找,这个变量的查找规则就被称之为作用域链
var n = 100
function fun() {
// return 是运行时行为
// VO的创建是编译时行为
console.log(n) // => undefined
return
var n = 200
}
fun()
function foo() {
var bar = baz = 100
/*
上述代码等价于
baz = 100
var bar = 100
所以bar是局部变量
而baz是隐式全局变量
*/
}
foo()
console.log(baz) // => 100
console.log(bar) // error
转载自:https://juejin.cn/post/7107617044836827144