执行上下文这类概念,就是大佬们说很重要,我等新手菜鸟完全无感的东西。下面是我根据《JS高级程序设计》结合部分大佬的解释整理的相关内容。
一、执行上下文概念
执行上下文个人理解是当前代码的执行环境,它会形成一个作用域。JavaScript中的运行环境大概包括三种情况。
- 全局环境:JavaScript代码运行时首先进入
- 函数环境:当函数被调用执行时进入
- eval(js执行字符串,问题很多,不建议使用,可忽略)
在一个JavaScript程序中,必定会产生多个执行上下文。JavaScript引擎会以函数调用栈(call stack)的方式来处理它们。栈底是全局上下文,而栈顶就是当前正在执行的上下文。
下面结合代码和图解来理解一下函数调用栈的执行。
let text = '全局'
function f1(){
let text1 = 'f1'
function f2() {
let text2 = 'f2'
console.log(text2)
}
f2()
console.log(text2)
}
f1()
function f3() {
let text3 = 'f3'
console.log(text3)
}
f3()
console.log(text)
下面总结一下函数调用栈:
- 单线程
- 执行中的上下文在栈顶执行,其余在下面等待继续执行
- 全局上下文在浏览器关闭时出栈
- 某个函数被调用时,新的执行上下文创建。
二、变量对象
我们通过上面的图例也可以很明显的看出,一个执行上下文的完成,分为了创建阶段和执行阶段。
-
创建阶段
在这个阶段中,执行上下文会分别创建变量对象,建立作用域链,以及确定this的指向。
-
代码执行阶段
创建完成之后,就会开始执行代码,这个时候,会完成变量赋值,函数引用,以及执行其他代码。
执行上下文的创建阶段产生了变量对象、作用域链和this指向等一系列难点,我觉得这应该就是执行上下文的重要之处吧。
this指向问题在上篇文章当中已做了详细解释,这里我们主要说一下变量对象。
变量对象的创建,依次经历了以下几个过程:
-
建立arguments对象。检查当前上下文中的参数,建立该对象下的属性与属性值。
-
检查当前上下文的函数声明,也就是使用function关键字声明的函数。在变量对象中以函数名建立一个属性,属性值为指向该函数所在内存地址的引用。如果函数名的属性已经存在,那么该属性将会被新的引用所覆盖。
-
检查当前上下文中的变量声明,每找到一个变量声明,就在变量对象中以变量名建立一个属性,属性值为undefined。
发现问题没有!对!变量提升!由变量对象的创建过程可知,function的声明比var、let等变量声明要先进行。
下面给大家看个例子,然后大家根据例子理解一下:
function test() {
console.log(a);
console.log(foo());
var a = 1;
function foo() {
return 2;
}
}
test();
//结果是输出 undefind , 2
下面按照执行顺序解释一下:
function test() {
// 先建立arguments对象,检验有无参数
function foo() {
return 2;
}// 函数定义提升到这里
var a; // 这里是变量定义,未赋值,为undefind
// 下面才是按照代码顺序的执行阶段
console.log(a);
console.log(foo());
a = 1;
}
test();
注意看代码注释!理解一下变量对象和变量提升。