深耕系列之执行上下文

312 阅读4分钟

上一篇文章讲到深耕系列之静态作用域和动态作用域提到函数作用域在定义时就决定了。大家可能还会有所迷茫,主要是对js执行过程不熟悉。

那么这篇文章会去深入理解执行上下文,理解js是如何执行的,执行上下文是执行的基础设置。

执行上下文介绍

简单来说,一段代码在执行过程中所需要的信息定义为执行上下文,javascript代码是在执行上下文中运行的。就如做菜时,锅碗瓢盆酱醋茶就相当于炒菜时的执行上下文。

那么执行上下文都记录了哪些内容?

执行上下文组成

上一篇文章提到的作用域,也是属于执行上下文的一部分,而执行上下文也包含了其他内容,不同的版本,它的组成还不一样:

在ES3中,包含三个部分

  • scope:作用域,也常被叫为作用域链
  • variable object:变量对象,用来存储变量
  • this值

在ES5中,改进了命名方式,也包含三个部分

  • lexical environment:词法环境,获取变量时使用
  • variable environment:变量环境,声明变量时使用
  • this值

在ES8中,this值被归纳到词法环境中,包含了更多的内容:

  • lexical environment:词法环境,获取变量或this值时使用
  • variable environment:变量环境,声明变量时使用
  • code evaluation state:用于恢复代码执行位置
  • Function: 任务是函数时用到,正在被执行的函数
  • ScriptOrModule:任务是脚本或模块时用到,正在被执行的代码
  • Realm:使用的基础库或内置对象
  • Generator:仅生成器上下文使用到,表示当前生成器

以上介绍了执行上下文在ECMAScript各个版本的定义,网上的文章往往都没有梳理这些内容。

在这里就不讲解这些组成的详细情况,我会在后续文章再来阐述这部分内容。

为了便于理解,我们常常对执行上下文进行分类,那么在javascript中,执行上下文都有哪些类型?

执行上下文类型

javascript总共有三种执行上下文类型:

  • 全局执行上下文:一整段script脚本执行时,会创建一个全局执行上下文。如浏览器在执行script脚本时,会先创建全局的window变量,然后设置this值为该全局对象。
  • 函数执行上下文:在执行一个函数时,会创建一个函数执行上下文,每个函数都有自己的执行上下文。
  • Eval执行上下文:执行在Eval函数内部的代码时,会创建Eval执行上下文,不过这个Eval很少使用。

当遇到函数中又调用其他函数,就会产生多个执行上下文,那么这些执行上下文是怎么关联起来的?

执行栈

执行栈用来存储代码运行中产生的所有执行上下文,具有先进后出的特性。

js引擎遇到javascript脚本时,先压入一个全局执行上下文,执行到函数,会再压入函数执行上下文。如果函数内部有调用其他函数,那么会继续压入函数执行上下文。函数执行完,会把栈顶的执行上下文弹出。

来看个例子:

let test = 1
function fn1() {
	console.log('fn1')
	fn2()
}
function fn2() {
	console.log('fn2')
}

fn1()

根据以上代码,执行栈的变更如下:

image.png

分析如下:

  1. 浏览器加载以上代码,js引擎会创建全局上下文并压入执行栈。
  2. 当遇到fn1()调用时,js引擎会创建fn1函数上下文并压入执行栈。
  3. 当遇到fn1函数内部调用fn2函数,js引擎会创建fn2函数上下文并压入执行栈。
  4. fn2函数执行完毕,js引擎将栈顶的fn2函数上下文弹出。
  5. fn1函数执行完毕,js引擎将栈顶的fn1函数上下文弹出。
  6. 以上代码执行完毕,js引擎将全局上下文弹出。

总结

以上就是我对js执行上下文的理解,可能你会好奇执行上下文中的this,词法环境,变量环境到底是什么,后续文章会再总结变量对象和this等内容。如果觉得对你有帮助,请帮忙点个赞+评论+收藏,关注不失联。