JavaScript变量提升和执行上下文
来,做个题 这段代码输出什么?
showTitle()
console.log(title)
var title = "CSDN"
function showTitle(){
console.log("showTitle 函数执行")
}
因为js是按顺序执行的,执行前两行的时候,会因为变量和方法没有定义而报错。
实际执行的结果却不是这样:
这里就是js的变量提升了
变量提升
变量提升:在js代码编译过程中,js引擎把变量和函数的声明部分提升到作用域的顶部的行为。 变量被提升后,会赋予初始值undefined。
// 变量提升部分(声明)
function showTitle(){
console.log("showTitle 函数执行")
}
var title = undefined
// 执行代码部分
title = "CSDN"
showTitle()
console.log("showTitle 函数执行")
js代码执行流程看变量提升
js代码执行流程,会被js引擎先进行编译,再执行。如下图所示。
执行上下文:js执行代码时的运行环境。比如调用一个函数,就会进入到此函数的执行上下文,确定函数执行期间用到的变量和函数等。
代码被编译后,生成两部分内容:执行上下文和可执行代码。执行上下文中包含变量环境和词法环境。
变量环境中存储着变量提升的内容(上方代码中的变量提升部分)。变量环境可以看做一个对象,存储着变量和方法的键值对。
showTitle()
console.log(title)
var title = "CSDN"
function showTitle(){
console.log("showTitle 函数执行")
}
编译阶段代码分析: ① 第一行和第二行不是声明操作,js引擎不做任何处理; ② 第三行,var声明操作,js引擎在变量环境中声明一个title的变量,初始化为undefined; ③ 第四行,函数定义操作,js引擎在变量环境中声明一个showTitle属性,并指向堆中函数声明的位置。 ④ 把声明外的代码编译为字节码。类似于下方代码
showTitle()
console.log(title)
title = "CSDN"
执行阶段代码分析:
① 第一行,函数执行,js引擎在变量环境中查找该函数。由于变量环境存在该函数的引用,因此开始执行,并输出。
② 第二行,输出语句执行,js引擎在变量环境中查找该变量。由于变量环境存在变量,输出undefined。
③ 第三行,赋值语句执行,赋值后,变量环境中的title变量,值更新为"CSDN"。
执行上下文
执行上下文是在js代码执行前编译后创建,分为三种: ① 当js执行全局代码时,编译并创建全局执行上下文,整个页面生存周期中仅此一份; ② 当调用一个函数时,函数内的代码被编译并创建函数执行上下文,函数执行完后进行销毁; ③ eval函数。
函数调用
虽然说函数调用是运行括号里的代码,但是具体的流程咱们来看一看:
var a = 3
function add(){
var b = 7
return a+b
}
add()
上述代码执行分析:
- 代码编译,创建全局执行上下文;(变量提升)
- 代码执行,给变量环境中a赋值3;
- add函数执行,从全局执行上下文中,取出add函数;
- 对函数内代码编译,创建该函数的执行上下文和可执行代码;
- 执行函数内代码。
此时我们看到有两个执行上下文,js引擎是通过栈进行管理的。栈是一种先进后出的数据结构,自行查阅。
js的执行上下文栈
执行上下文创建好后,js引擎会将其压入栈中,通常把这种管理执行上下文的栈成为执行上下文栈,又称调用栈。调用栈有可存储的容量限制。 上代码:
var a = 3
function add(b,c){
return b+c
}
function addAll(b,c){
var d = 6
var result = add(b,c)
return a+result+d
}
addAll(4,5)
- 代码编译,创建全局执行上下文,将其压入栈底;
a、add、addAll都保存到了全局执行上下文的变量环境中。压入栈底后,执行全局代码。
- 执行a的赋值,从undefined替换为3,以后代码再访问a,将为3;
- 执行addAll函数,调用该函数时,js引擎会编译该函数,为其创建一个函数执行上下文,入栈。
- 函数执行上下文创建后,函数进入执行阶段,首先给d赋值6,执行到add函数,为其编译创建执行上下文,入栈。
- add函数执行,返回值后执行完成,从栈顶弹出。将result的值设置为9。
- 执行addAll函数,返回值后,执行完成,从栈顶弹出。最终只有全局执行上下文。js执行结束。
调用栈在浏览器中的位置
右侧Call Stack为调用栈的信息:栈底是anonymous,全局的函数入口;中间是addAll函数;栈顶为add函数。清晰反应函数的调用(setup为vue3的函数,可以忽略)。