JS专精-执行上下文/函数

248 阅读5分钟

JS专精-执行上下文/函数

参考文章(1)

什么是执行上下文

执行上下文是一种对Javascript代码执行环境的一种抽象概念,也就是说只要有Javascript代码运行,那么它就一定是运行在执行上下文中。

Javascript一共有三种执行上下文:

  • 全局执行上下文。这是一个默认的或者说基础的执行上下文,所有不在函数中的代码都会在全局执行上下文中执行。它会做两件事:创建一个全局的window对象(浏览器环境下),并将this的值设置为该全局对象,另外一个程序中只能有一个全局上下文。
  • 函数执行上下文。每次调用函数时,都会为该函数创建一个执行上下文,每一个函数都有自己的一个执行上下文,但注意是该执行上下文是在函数被调用的时候才会被创建。函数执行上下文会有很多个,每当一个执行上下文被创建的时候,都会按照他们定义的顺序去执行相关代码。
  • Eval函数执行上下文。在eval函数中执行的代码也会有自己的执行上下文,但由于eval函数不会被经常用到,这里就不做讨论了。(译者注:eval函数容易导致恶意攻击,并且运行代码的速度比相应的替代方法慢,因为不推荐使用)。

什么是执行栈

执行栈,在其他编程语言中也被称为“调用栈”,这是栈数据结构,被用来储存在代码运行阶段创建的所有的执行上下文。

JS引擎对上下文做了什么

当Javascript引擎(Javascript代码的解释器)开始执行你第一行Javascript脚本代码的时候,它就会创建一个全局执行上下文然后将它压到执行栈中。每当引擎碰到一个函数调用的时候,它就会创建一个函数执行上下文,然后将这个执行上下文压到执行栈中。

怎么创建上下文

执行上下文的创建分为两个阶段:

  • 创建阶段
  • 执行阶段

创建阶段包括以下几个方面:

  • 创建词法环境
  • 创建变量环境

词法环境

词法环境就是一种标识符—变量映射的结构(这里的标识符指的是变量/函数的名字,变量是对实际对象[包含函数和数组类型的对象]或基础数据类型的引用)。

每一个词法环境由下面三部分组成:

  • 环境记录;
  • 外部环境引用;
  • 绑定this;

环境记录

所谓的环境记录就是词法环境中记录变量和函数声明的地方。环境记录也有两种类型:

  • 声明类环境记录。顾名思义,它存储的是变量和函数声明,函数的词法环境内部就包含着一个声明类环境记录。
  • 对象环境记录。全局环境中的词法环境中就包含的就是一个对象环境记录。除了变量和函数声明外,对象环境记录还包括全局对象(浏览器的window对象)。因此,对于对象的每一个新增属性(对浏览器来说,它包含浏览器提供给window对象的所有属性和方法),都会在该记录中创建一个新条目。
  • 环境记录对象在创建阶段也被称为变量对象(VO),在执行阶段被称为活动对象(AO)。之所以被称为变量对象是因为此时该对象只是存储执行上下文中变量和函数声明,之后代码开始执行,变量会逐渐被初始化或是修改,然后这个对象就被称为活动对象。(变量提升的根本原因
  • 对函数而言,环境记录还包含一个arguments对象,该对象是个类数组对象,包含参数索引和参数的映射以及一个传入函数的参数的长度属性。

外部环境引用

对于外部环境的引用意味着在当前执行上下文中可以访问外部词法环境。也就是说,如果在当前的词法环境中找不到某个变量,那么Javascript引擎会试图在上层的词法环境中寻找。(作用域链)

绑定this

在词法环境创建阶段中,会确定this的值。

  • 在全局执行上下文中,this值会被映射到全局对象中(在浏览器中,也就是window对象)。
  • 在函数执行上下文中,this值取决于谁调用了该函数

变量环境

其实变量环境也是词法环境的一种,它的环境记录包含了变量声明语句在执行上下文中创建的变量和具体值的绑定关系。

如上所述,变量环境也是词法环境的一种,因此它具有词法环境所有的属性。

在ES6中,词法环境和变量环境的不同就是前者用来存储函数声明和变量声明(let和const)绑定关系,后者只用来存储var声明的变量绑定关系。

执行阶段

在这个阶段,将完成所有变量的赋值操作,然后执行代码。(压栈弹栈)

小结

  1. 输入一段JS代码,经过编译后,会生成两部分内容:执行上下文和可执行代码。
  2. JS引擎首先创建上下文(词法环境/变量环境),创建全局上下文之后开始执行代码,
  3. 进入执行代码阶段,先压入全局上下文,之后看见函数调用就创建并压入函数上下文,期间可能会更新全局上下文