JavaScript进阶之执行上下文和执行栈

js引擎的执行过程

 

执行上下文和执行栈属于js引擎的执行过程的预编译阶段。

执行上下文(Execution Context)

执行上下文是当前 JavaScript 代码被解析和执行时所在环境的抽象概念。可以理解为当执行代码时做的准备工作。

 

执行上下文按照运行环境被分成三类:

  • 全局执行上下文(JS代码加载完毕后,进入代码预编译即进入全局环境)

  • 函数环境执行上下文(函数调用执行时,进入该函数环境,不同的函数则函数环境不同)

  • eval执行上下文(不建议使用,会有安全,性能等问题)

对于每个执行上下文,都有三个重要属性:

  • 变量对象(Variable object,VO),存储了在上下文中定义的变量和函数声明。在全局执行上下文中指window,函数执行上下文中就是活动对象(AO)
  • 作用域链(Scope chain),和原型链类似,当查找变量时,会先在当前上下文变量对象中查找,如果没有会去父级上下文变量对象中查找,直到全局上下文。这样由多个执行上下文的变量对象构成的链表就叫做作用域链
  • this

VO:变量对象(Variable object);

AO:活动对象(activation object),当进入函数执行上下文时,这个函数执行上下文的变量对象才会被激活,所以才叫 activation object,而只有被激活的变量对象,也就是活动对象上的各种属性才能被访问(进入函数上下文时创建活动对象)。

执行栈

执行栈,也叫调用栈,具有 LIFO(后进先出)结构,用于存储在代码执行期间创建的所有执行上下文。

首次运行JS代码时,会创建一个全局执行上下文并Push到当前的执行栈中。每当发生函数调用,引擎都会为该函数创建一个新的函数执行上下文并Push到当前执行栈的栈顶。

根据执行栈LIFO规则,当栈顶函数运行完成后,其对应的函数执行上下文将会从执行栈中Pop出,上下文控制权将移到当前执行栈的下一个执行上下文。

全局执行上下文在浏览器关闭或者标签关闭时才会出栈。

 1 var a = 'Hello World!';
 2 
 3 function first() {  
 4   console.log('Inside first function');  
 5   second();  
 6   console.log('Again inside first function');  
 7 }
 8 
 9 function second() {  
10   console.log('Inside second function');  
11 }
12 
13 first();  
14 console.log('Inside Global Execution Context');
15 
16 // Inside first function
17 // Inside second function
18 // Again inside first function
19 // Inside Global Execution Context

 

创建过程

 

 

创建阶段就是确定三个重要上下文属性的过程。

创建变量对象

  1. 创建arguments对象,检查当前上下文中的参数,建立该对象的属性与属性值,仅在函数环境(非箭头函数)中进行,全局环境没有此过程

  2. 检查当前上下文的函数声明,按代码顺序查找,将找到的函数提前声明,如果当前上下文的变量对象没有该函数名属性,则在该变量对象以函数名建立一个属性,属性值则为指向该函数所在堆内存地址的引用,如果存在,则会被新的引用覆盖。

  3. 检查当前上下文的变量声明,按代码顺序查找,将找到的变量提前声明,如果当前上下文的变量对象没有该变量名属性,则在该变量对象以变量名建立一个属性,属性值为undefined;如果存在,则忽略该变量声明

变量提升的原因:

在创建阶段,函数声明存储在环境中,而变量会被设置为 undefined(在 var 的情况下)或保持未初始化(在 let 和 const 的情况下)。所以这就是为什么可以在声明之前访问 var 定义的变量(尽管是 undefined ),但如果在声明之前访问 let 和 const 定义的变量就会提示引用错误的原因。这就是所谓的变量提升。

从创建顺序看,函数提升优先于变量提升。

创建作用域链

由多个执行上下文的变量对象构成的链表就叫做作用域链。

当函数创建的时候,函数有一个内部属性会保存所有父变量对象到其中。

进入函数上下文,创建 VO/AO 后,就会将活动对象添加到作用链的前端。

所以:

  • 作用域链的第一项永远是当前作用域(当前上下文的变量对象或活动对象);

  • 最后一项永远是全局作用域(全局执行上下文的活动对象);

  • 作用域链保证了变量和函数的有序访问,查找方式是沿着作用域链从左至右查找变量或函数,找到则会停止查找,找不到则一直查找到全局作用域,再找不到则会抛出引用错误。

确定This指向

全局执行上下文中变量对象的this属性指向为window,函数上下文则较为复杂,以后会详细介绍。

执行阶段

完成对所有变量的分配,最后执行代码。

总览

 参考文档:

      理解JavaScript 中的执行上下文和执行栈

      JavaScript深入之执行上下文栈

      JavaScript深入之变量对象

      JavaScript深入之作用域链

      js引擎的执行过程(一)

 

 

posted @ 2019-01-30 17:11  starlightkk  阅读(292)  评论(0编辑  收藏  举报