深入浅出JavaScript变量提升和执行上下文(1)

107 阅读4分钟

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()

上述代码执行分析:

  1. 代码编译,创建全局执行上下文;(变量提升) 在这里插入图片描述
  2. 代码执行,给变量环境中a赋值3;
  3. add函数执行,从全局执行上下文中,取出add函数;
  4. 对函数内代码编译,创建该函数的执行上下文和可执行代码;
  5. 执行函数内代码。 在这里插入图片描述

此时我们看到有两个执行上下文,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)
  1. 代码编译,创建全局执行上下文,将其压入栈底; a、add、addAll都保存到了全局执行上下文的变量环境中。压入栈底后,执行全局代码。 在这里插入图片描述
  2. 执行a的赋值,从undefined替换为3,以后代码再访问a,将为3;
  3. 执行addAll函数,调用该函数时,js引擎会编译该函数,为其创建一个函数执行上下文,入栈。 在这里插入图片描述
  4. 函数执行上下文创建后,函数进入执行阶段,首先给d赋值6,执行到add函数,为其编译创建执行上下文,入栈。 在这里插入图片描述
  5. add函数执行,返回值后执行完成,从栈顶弹出。将result的值设置为9。 在这里插入图片描述
  6. 执行addAll函数,返回值后,执行完成,从栈顶弹出。最终只有全局执行上下文。js执行结束。

调用栈在浏览器中的位置

在这里插入图片描述

右侧Call Stack为调用栈的信息:栈底是anonymous,全局的函数入口;中间是addAll函数;栈顶为add函数。清晰反应函数的调用(setup为vue3的函数,可以忽略)。

文章参考:time.geekbang.org/column/intr…