函数执行上下文
函数调用时,才会创建上下文,分为两个阶段:
- 创建阶段
-
创建VO(variable object)
在 VO 中,变量和函数声明都会被存储为属性名,它们的值分别为 undefined 或函数对象。在函数执行时,JavaScript 引擎会按照以下顺序对 VO 进行处理:
- 创建 VO。
- 处理函数声明。将函数声明添加到 VO 中,并将函数对象赋值给相应的属性。
- 处理变量声明。将变量声明添加到 VO 中,并将 undefined 赋值给相应的属性。
- 处理形参。将函数参数添加到 VO 中,并将传入的实参赋值给相应的属性。
- 执行函数体内的代码。
-
链接作用域链(scope)
-
确定this指向
- 执行阶段
- 激活VO为AO(activation object),vo和ao其实是一个变量对象在函数生命周期中的不同名字
VO的具体结构,包括形参、变量声明和Function声明:
function d(b=1){
var a=0;
function c(){}
}
=====> 该匿名函数的VO结构如下
{
b:undefined
a:undefined
c:<referrence fo function>
}
上述上下文栈状态如下
c:
{
VO:{},
scope: [Global_AO,d_AO],
this: ? | globalThis
}
d:
{
d_AO: {
arguments:{
0:1
length:1
},
b:1,
a:0,
c: <referrence fo function>
},
scope:[Global_AO],
this: ? | globalThis
}
global:
{
Global_AO: {
...windows | ...global, //不是真的解构,只是为了表示
d: <referrence fo function>
}
this: globalThis
}
这就可以解释为什么会有变量提升了:
- 函数在进入创建阶段,就会创建变量对象,其中保存了所声明的变量,只是赋值并没有提升,所以变量都是undefined而函数则是响应的引用
- 其中有一点,当函数名与变量同名时,VO会忽略变量声明防止覆盖函数,如下:
function(){
console.log(c) //function c
var c;
function c(){}
console.log(c) //function c
c=1;
console.log(c) //1
}
不过,这个是创建阶段提升的结果,执行阶段c被赋值还是会被修改的
- 而如果是形参和变量重名的话,形参会覆盖变量,但是有时会报错,如下:
function(a){
let a;
}
这个等同于
let a;
var a; // SyntaxError: Identifier 'a' has already been declared