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出,上下文控制权将移到当前执行栈的下一个执行上下文。
全局执行上下文在浏览器关闭或者标签关闭时才会出栈。
var a = 'Hello World!'; function first() {
console.log('Inside first function');
second();
console.log('Again inside first function');
} function second() {
console.log('Inside second function');
} first();
console.log('Inside Global Execution Context'); // Inside first function
// Inside second function
// Again inside first function
// Inside Global Execution Context
创建过程
创建阶段就是确定三个重要上下文属性的过程。
创建变量对象
创建arguments对象,检查当前上下文中的参数,建立该对象的属性与属性值,仅在函数环境(非箭头函数)中进行,全局环境没有此过程
检查当前上下文的函数声明,按代码顺序查找,将找到的函数提前声明,如果当前上下文的变量对象没有该函数名属性,则在该变量对象以函数名建立一个属性,属性值则为指向该函数所在堆内存地址的引用,如果存在,则会被新的引用覆盖。
检查当前上下文的变量声明,按代码顺序查找,将找到的变量提前声明,如果当前上下文的变量对象没有该变量名属性,则在该变量对象以变量名建立一个属性,属性值为undefined;如果存在,则忽略该变量声明
变量提升的原因:
在创建阶段,函数声明存储在环境中,而变量会被设置为 undefined
(在 var
的情况下)或保持未初始化(在 let
和 const
的情况下)。所以这就是为什么可以在声明之前访问 var
定义的变量(尽管是 undefined
),但如果在声明之前访问 let
和 const
定义的变量就会提示引用错误的原因。这就是所谓的变量提升。
从创建顺序看,函数提升优先于变量提升。
创建作用域链
由多个执行上下文的变量对象构成的链表就叫做作用域链。
当函数创建的时候,函数有一个内部属性会保存所有父变量对象到其中。
进入函数上下文,创建 VO/AO 后,就会将活动对象添加到作用链的前端。
所以:
作用域链的第一项永远是当前作用域(当前上下文的变量对象或活动对象);
最后一项永远是全局作用域(全局执行上下文的活动对象);
作用域链保证了变量和函数的有序访问,查找方式是沿着作用域链从左至右查找变量或函数,找到则会停止查找,找不到则一直查找到全局作用域,再找不到则会抛出引用错误。
确定This指向
全局执行上下文中变量对象的this属性指向为window,函数上下文则较为复杂,以后会详细介绍。
执行阶段
完成对所有变量的分配,最后执行代码。
总览
参考文档:
最新文章
- XVI Open Cup named after E.V. Pankratiev. GP of Ukraine
- php调用阿里大鱼 接口curl
- 【转载】SQL Server 2008 r2 中 SQL语句中单引号转义
- JavaScript Lib Interface (JavaScript系统定义的接口一览表)
- ubuntu安装python一些安装包
- 看那记不住命令的猿,如何使用GitHub
- JavaScript函数 bind call apply区别
- js获取中英文长度
- error -27257: Pending web_reg_save_param/reg_find/create_html_param[_ex] request(s) detected and reset at the end of iteration number 1
- android设置中的Preferencescreen使用方法介绍与分析
- java程序在没有java环境的电脑上执行的方法(关键词jar,exe)
- nasm预处理器(3)
- 【*和&;】复习-杂七杂八代码收集
- XML CDATA识别“<;,>;”
- 生信工具汇总--OMICtools
- 每天一个Linux命令(05):tail命令
- redis踩坑记录
- Nginx详解十六:Nginx场景实践篇之缓存服务
- 菜鸟入门【ASP.NET Core】11:应用Jwtbearer Authentication、生成jwt token
- Ubuntu安装SSH SERVER