hzzzzzzzq / Blog

这是我记录博客的地方,希望能多写一些技术博客,JS、ES、React、Vue等
14 stars 0 forks source link

深入理解JavaScript系列(2):执行上下文栈 #43

Open hzzzzzzzq opened 2 years ago

hzzzzzzzq commented 2 years ago

JavaScript 深入之执行上下文栈

JS 是否是顺序执行?

JavaScript 代码的执行顺序,在写过该语言的人眼中,都会认为是顺序执行的。

var fn = function () {
  console.log('first function');
};

fn(); // first function

var fn = function () {
  console.log('second function');
};
fn(); // second function

完美的顺序执行,但是我们稍微修改一下呢?

function fn() {
  console.log('first function');
}

fn(); // second function

function fn() {
  console.log('second function');
}
fn(); // second function

打印结果真的是出人意料啊,两个都为 second function。这又是为什么呢?

因为我们的 JavaScript 引擎执行代码是通过一段一段的执行,而不是一行一行的执行。所以当执行一段代码时,就会进入一个准备工作,比如变量提升或函数提升。

现在我们了解到了,执行方式,那我们再来看看,JS 是怎么一段一段的执行的。

执行上下文

我们先了解一下执行上下文,执行上下文就是当前 JavaScript 代码被解析和执行时所在环境的抽象概念,JavaScript 中运行任何的代码都是在执行上下文中运行。

JavaScript 中的运行环境包括三种情况:

举一个小例子,当执行一个函数时,就会进行准备工作,而这个准备工作,专业一点的说法就是执行上下文。

什么是执行上下文栈?

那么函数多了,如何来管理这么多执行上下文呢?

所以 JavaScript 引擎创建了执行上下文栈来管理执行上下文

我们使用一个 ECStack 数组,作为执行上下文栈的行为模拟工具。

首先,当 JavaScript 开始解析执行代码时,先遇到一个全局代码,所以在初始化我们的执行栈时,就会向执行上下文栈压入一个全局执行栈(globalContext)。 并且只有整个程序执行结束时,ECStack 才会被清空,所以在程序结束之前,globalContext 会一直存在于 ECStack 的最底部。

ECStack = []; // 创建
ECStack = [globalContext]; // 压入全局执行栈

我们写一段代码,来测试我们的执行栈。

function fn1() {
  console.log('fn1 code');
}
function fn2() {
  fn1();
}
function fn3() {
  fn2();
}
fn3();

当执行函数时,就会创建一个执行上下文,并且压入栈,执行完毕,就会从栈中弹出。我们用数组来模拟栈,所以是先进后出。

所以我们来看看时怎么执行栈的吧:

// 一开始执行时,栈中有全局执行栈
ECStack = [
  globalContext,
]
// 首先
// fn3() 执行,压入栈
ECStack.push(<fn3> functionContext);
ECStack = [
  globalContext,
  <fn3> functionContext
]

// fn3 函数中 执行了 fn2
ECStack.push(<fn2> functionContext);
ECStack = [
  <fn2> functionContext
  <fn3> functionContext,
  globalContext
]
// fn2 函数中执行了 fn1
ECStack.push(<fn1> functionContext);
ECStack = [
  <fn1> functionContext,
  <fn2> functionContext,
  <fn3> functionContext,
  globalContext
]

// fn1 执行完毕
ECStack.pop();

// fn2 执行完毕
ECStack.pop();

// fn3 执行完毕
ECStack.pop();

// 最后就是继续执行 JavaScript 代码,知道全部执行完毕,才弹出 globalContext

深入执行上下文栈,就介绍到这里了。