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
创建过程
创建阶段就是确定三个重要上下文属性的过程。
创建变量对象
- 创建arguments对象,检查当前上下文中的参数,建立该对象的属性与属性值,仅在函数环境(非箭头函数)中进行,全局环境没有此过程
- 检查当前上下文的函数声明,按代码顺序查找,将找到的函数提前声明,如果当前上下文的变量对象没有该函数名属性,则在该变量对象以函数名建立一个属性,属性值则为指向该函数所在堆内存地址的引用,如果存在,则会被新的引用覆盖。
- 检查当前上下文的变量声明,按代码顺序查找,将找到的变量提前声明,如果当前上下文的变量对象没有该变量名属性,则在该变量对象以变量名建立一个属性,属性值为undefined;如果存在,则忽略该变量声明
变量提升的原因:
在创建阶段,函数声明存储在环境中,而变量会被设置为 undefined
(在 var
的情况下)或保持未初始化(在 let
和 const
的情况下)。所以这就是为什么可以在声明之前访问 var
定义的变量(尽管是 undefined
),但如果在声明之前访问 let
和 const
定义的变量就会提示引用错误的原因。这就是所谓的变量提升。
从创建顺序看,函数提升优先于变量提升。
创建作用域链
由多个执行上下文的变量对象构成的链表就叫做作用域链。
当函数创建的时候,函数有一个内部属性会保存所有父变量对象到其中。
进入函数上下文,创建 VO/AO 后,就会将活动对象添加到作用链的前端。
所以:
- 作用域链的第一项永远是当前作用域(当前上下文的变量对象或活动对象);
- 最后一项永远是全局作用域(全局执行上下文的活动对象);
- 作用域链保证了变量和函数的有序访问,查找方式是沿着作用域链从左至右查找变量或函数,找到则会停止查找,找不到则一直查找到全局作用域,再找不到则会抛出引用错误。
确定This指向
全局执行上下文中变量对象的this属性指向为window,函数上下文则较为复杂,以后会详细介绍。
执行阶段
完成对所有变量的分配,最后执行代码。
总览
参考文档:
原文地址:https://www.cnblogs.com/shaunyang/p/10337099.html