执行上下文和执行栈
执行上下文的类型
全局执行上下文: 只有一个,浏览器中就是window对象,this指向这个全局对象
函数执行上下文: 当函数被调用的时候这个函数执行上下文就会被创建,每次调用都会创建一个新的函数执行上下文。
执行栈(Execution context stack)
既然是执行栈,一定是后进先出,是用来存储代码执行期间的执行上下文的。
首次运行js代码的时候,会创建一个全局执行上下文(globalContext)push到执行栈底。然后函数执行的时候把创建的函数执行上下文push到执行栈里。当这个函数执行完成时,函数执行上下文pop出去,控制权交给下一个执行上下文。当所有代码执行完成时,全局上下文pop,运行结束。
执行上下文的创建
执行上下文的创建分为两个阶段,创建阶段和执行阶段
创建阶段
1.确定this的值,也被称为this binding
全局执行上下文this指向全局对象,在浏览器中指向的时window,在node里面指向的当前的module对象。
函数执行上下文取决于函数的调用方式。
2.lexicalEnv环境(词法环境)创建
词法环境有两部分组成:
1.环境记录:存储变量和函数声明的实际位置。
2.对外部环境的引用:可以访问其外部词法环境
词法环境的两种类型
全局环境:外部环境引用为null。里面包含一个全局对象,用户自定义的全局变量,自带的方法。
函数环境:用户在函数中定义的变量被储存在环境记录中,包含了arguments对象,外部环境的引用可以是全局对象,也可以是其所在的外部函数。(函数里面套函数的形式)
3.varEnv环境(变量环境)创建
变量环境其实是专为var关键字服务的,变量环境本质上也是一个词法环境。
而使用var关键字声明的变量会存在变量提升的问题。
在创建阶段,用var 声明的变量存在varEnv中,此时访问,尽管值为undefined,但也可以访问的到。而使用const和let声明的变量是存在lexicalEnv中,保持未初始化的状态的,这时候访问就会抛错。
这就是变量提升的原因。
下面我们贴出一段代码,来看一下进入执行上下文后变量的初始化顺序
function foo(a) {
var b = 2;
function c() {}
var d = function() {};
b = 3;
}
foo(1);
此时活动对象(tips:函数所接收参数与函数内声明的变量并不冲突。)
AO = {
arguments: {
0: 1,
length: 1
},
a: 1,
b: undefined,
c: reference to function c(){},
d: undefined
}
执行阶段
这个阶段完成对所有变量的分配,最后执行代码。所以上述代码执行阶段的活动对象为:
AO = {
arguments: {
0: 1,
length: 1
},
a: 1,
b: 3,
c: reference to function c(){},
d: reference to FunctionExpression "d"
}