前言
本文试图通过一张原理图串联一系列JS执行中的概念,这些概念包括“变量提升、执行上下文、调用栈、作用域、作用域链、块状作用域、词法作用域、闭包”等等。我将说明这些概念都表示这张原理图的一部分,大家理解之后就可以跟面试官对线,不论面试官怎么问,你都可以画出这张图,然后说:“这一切都要从V8的底层说起…”
记住这张图
解释这张图
首先,我打算对这张图中要表达的内容做一个综述,然后再分别举例解释上述概念。
一段JS代码的执行分为编译阶段和执行阶段。在编译阶段中,代码经过编译会生成两部分内容:执行上下文和可执行代码。执行上下文是JS代码的执行环境,多个执行上下文将被依次压入一个栈结构,这个栈结构称作调用栈。执行上下文中包含三部分:变量环境、词法环境和this变量。变量环境是一个对象,保存var变量和function函数声明。词法环境也是一个栈结构,栈成员是一个对象,保存let和const变量。在上下文内部,变量访问总是从词法环境的栈顶开始,从栈顶到栈底,然后到变量环境。在上下文之间,通过outer变量访问下一级上下文,直到全局上下文结束。这个变量访问顺序就是作用域链。this不参与作用域链查询,它的值取决于四种赋值过程。上下文之间偶尔会插入一个闭包,闭包通常跟楼上的函数上下文一起入栈,它不是一个上下文结构,但保存着内部函数创建时所引用的外部函数变量。
概念1:变量提升
foo()
function foo() {console.log('foo')
}
console.log(a)
var a = 1
/* 执行结果
foo
undefined
*/
变量提升是指JS会将var变量声明和function函数声明提升至代码开头的行为,原理如下:
JS代码执行分为编译阶段和执行阶段。编译阶段中,JS引擎会创建上下文并读取var变量声明和function函数声明保存至变量环境。在执行阶段,JS引擎会访问变量环境,执行代码。
对照原理图,我们可以发现,变量提升的核心原因就是JS执行分为两个阶段以及提前将变量存入了变量环境。
案例中的代码可以分别声明部分和执行部分,这两部分是在不同阶段执行的。
// 声明部分
function foo() {console.log('foo')
}
var a = undefined
// 执行部分
foo()
cons