前言

我们先不看这个标题,来看下面这段代码是怎么运行的:

var num1=1
var num1=2
var result=num1 num2
console.log(result)

我相信,小伙伴都知道打印的是3把,这也太简单了,但是我们思考下,这段代码执行时,到底进行了怎样的操作,这里我们要引进一个概念:初始化全局对象和执行上下文

初始化全局对象(GO)

JS引擎在解析代码的时候,会在堆内存中创建一个全局对象叫:Gobal Object(GO)

  • 该对象所有的作用域都可以访问
  • 里面会包含Date Array String Number setTimeout setInterval 等等
  • 还有一个windows属性指向本身

执行上下文

代码运行时:js引擎(这里考虑V8引擎)会创建执行上下文栈(Execution context stack)简称ECS,它就是执行代码的调用栈

执行上下文分为:

  • 全局执行上下文( GEC )
  • 函数执行上下文( FEC )

这里我们先分析下全局执行上下文,因为我们开头那段代码是全局代码

全局执行上下文

在执行全局代码的时候,会创建全局执行上下文( GEC ),GEC会被放入ECS中去执行 GEC被放入ECS

包含两部分:

  • 第一部分:在代码执行前,也就是V8引擎将parser转成AST的过程中 ( 这里会涉及到V8引擎在解析js代码的过程,会开篇专题讲这个),会将全局定义的变量放入GO中,但是并不会赋值,因为是解析阶段 (pass:这个过程也被叫做变量的作用域提升
  • 第二部分:执行代码,并为GO里面的变量赋值,或者执行其他函数 流程图如下:图中执行前是上面描述的第一部分,开始执行代码是第二部分

Java Script遇到函数代码如何执行?

当js引擎遇到函数执行时,会创建一个 函数执行上下文( Functional Execution Contex ) 简称FEC,并压入到执行上下文栈ECS

FEC中包含三部分内容:

  • 第一部分:VO(variable object ) 对象,其实也是AO( Activation Object )
  • 第二部分:作用域链(scope chain):由自己的VO 父级的VO,查找时会一层一层去查找
  • 第三部分:this绑定( 这里不做详细描述,后续会出专题 )

作用域是解析编译的时候就决定了,并不是执行调用的时候来我们看一段代码,一起看一下函数的执行过程,一起来思考下这段代码的运行结果

var message = "Global"
function foo() {
  console.log(message)
}
function bar() {
  var message = "Bar"
  foo()
}
bar()

画个图分析一下吧

首先初始化全局对象GO,里面有Array、date、setTime等等,还有自己定义的 message对象初始值为undefined,foo和bar函数对象

开始执行代码前,会创建一个执行上下文栈ESC,开始执行全局代码,所以会创建一个全局执行上下文栈GEC,将GEC压入栈底,全局执行上下文包括两部分:
第一部分代码执行前的VO对象(这里的VO指向的是GO)
第二部分是开始执行代码,执行第一行时 var message="Global",GO对象里的message就被改为Global 

执行完第一行后,开始执行第9行代码 bar(),这里是函数的调用执行,js引擎会创建一个函数执行上下文FEC,压入栈中,FEC包含三部分:

第一部分:VO对象,这里指向bar自己的AO对象(包括形参和函数中定义的变量)
第二部分:作用域链scope chain,自己的VO对象 parent VO对象

第三部分:this绑定,这里是指向windows(这个后续会开专题讲)
开始执行bar函数里面的代码,var message="Bar",会将bar的AO对象的message变量值从undefined改为"Bar",接下来在执行foo()函数,注意:此时bar函数还没弹出栈,因为foo函数还在执行

访问一个变量的时候,会沿着作用域链一层一层往上找,最后没有找到则会报错

执行到bar函数的最后一行代码是foo(),此时又是一个函数的调用执行,又会创建一个foo的函数执行上下文,也包含上述的三部分:VO对象(指向foo的AO对象) 作用域链 和this绑定
开始执行foo函数代码console.log(message),当打印message的时候,会沿着作用域链一层层找foo的作用域链是自己的AO 父级的VO(也就是GO对象),自己的AO对象为空,所以找到GO里面的message变量,最终打印"Global" 

执行完后,函数执行上下文执行完之后,就会弹出栈,foo的FEC先弹出栈,然后bar的FEC弹出栈,他们自个的AO对象最终也被释放

环境变量和记录

我们上述所说的VO是基于早期的ECMAS规范,官网是这样说的:

 这里借助coderwhy的翻译:每个执行上下文都关联一个变量对象(Vriable Object),在源代码中变量和函数的声明会被作为属性加入到变量对象中,对于函数来说,参数也会被加入到VO中但是ECMA5以后的版本,官网做了一些词汇用语的修改 

每个执行上下文都会关联一个变量环境(variable Environment),在执行代码中变量和函数的声明会被当做环境记录(Environment Record)加入到变量环境中。
对于函数代码,形参也会被当做环境记录加入到变量环境中

总结:

  • 在访问一个变量时,会沿着作用域链一层层往上找,最终没找到,会报错:未定义
  • 作用域以及父级作用域是代码在编译阶段就已经确定了,和调用位置没关系

汇总一些名词解释,我们来解释一下:

名词 解释
ECS 执行上下文栈 (Execution Context Stack),也可称为调用栈
GEC 全局执行上下文(Global Execution Context),在执行全局代码前创建
FEC 函数执行上下文(Functional Execution Context),在执行函数前创建
VO Variable Object,早期ECMA规范中的变量环境,对应Object
VE Variable Environment,最新ECMA规范中的变量环境,对应环境记录
GO 全局对象(Global Object),解析全局代码时创建,GEC中关联的VO就是GO
AO 活动对象(Activation Object),VO被激活就变成了AO,VO和AO本质上是一个东西,它们其实都是同一个对象,只是处于执行环境的不同生命周期

到此这篇关于深入学习JavaScript执行上下文的文章就介绍到这了,更多相关JS执行上下文内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

深入学习JavaScript执行上下文的更多相关文章

  1. 基于JavaScript编写一个图片转PDF转换器

    本文为大家介绍了一个简单的 JavaScript 项目,可以将图片转换为 PDF 文件。你可以从本地选择任何一张图片,只需点击一下即可将其转换为 PDF 文件,感兴趣的可以动手尝试一下

  2. HTML5数字输入仅接受整数的实现代码

    这篇文章主要介绍了HTML5数字输入仅接受整数的实现代码,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  3. amaze ui 的使用详细教程

    这篇文章主要介绍了amaze ui 的使用详细教程,本文通过多种方法给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  4. html5简介_动力节点Java学院整理

    这篇文章主要介绍了html5简介,用于指定构建网页的元素,这些元素中的大多数都用于描述网页内容,有兴趣的可以了解一下

  5. ios – 使用watchOS 2在Apple Watch上渲染折线图

    我正在尝试使用watchOS2在AppleWatch上渲染线条/步骤图.与iOS9不同,watchOS2不支持Quartz.它只支持CoreGraphics.我尝试编写一些代码来绘制折线图但我得到一个错误“CGContextRestoreGState:无效的上下文0x0.这是一个严重的错误.这个应用程序,或它使用的库,正在使用无效的上下文,从而有助于整体系统稳定性和可靠性降低.这个通知是礼貌的:请

  6. ios 8 Homescreen webapp,关闭和打开iPad停止javascript

    我有一个适用于iPad的全屏HTML5网络应用程序,并且刚刚安装了IOS8来试用它,它一切正常,直到你关闭并重新启动iPad.一旦web应用程序重新启动javascript就会停止并加载新页面不会重新启动它.在iPad上的Safari中打开同一页面时,关闭和打开iPad会继续按预期工作.其他人注意到了这个或想出了一个解决方案吗?解决方法这似乎是我在iOS8.1.1更新中解决的.

  7. performBlockAndWait在iOS 7上使用专用队列死锁的子上下文

    如果它出现在主队列中,那么你就会遇到问题,因为你正在阻止它.假设情况并非如此,您是否可以从Xcode共享显示截止日期的线程堆栈?一旦我好好看看那个堆栈,我会更新我的答案.Quellish是正确的,你没有正确插入孩子.该子MOC上的任何活动都应该在-performBlock:或-performBlockAndWait:中.我会扩展-performBlockAndWait:来覆盖对象的整个创建和决策,而不仅仅是保存.更新1-createWithAttributes是什么:inManagedobjectCont

  8. ios – 合并子上下文时的NSObjectInaccessbileExceptions

    我尝试手动重现,但失败了.是否有其他可能发生这种情况的情况,是否有处理此类问题的提示?解决方法在创建子上下文时,您可以尝试使用以下行:

  9. ios – CGContextClear警告

    解决方法我想说它肯定与CGContextClear有关.它保留了你在记忆中的所有东西.虽然您的上下文未被清除或释放,但它会将该上下文中定义的元素保留在内存中.您使用的是哪个版本的iOS?

  10. iOS 6 javascript与object.defineProperty的间歇性问题

    当访问使用较新的Object.defineProperty语法定义属性的对象的属性时,有没有其他人注意到新iOS6javascript引擎中的间歇性错误/问题?https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/defineProperty我正在看到javascript失败的情况,说

随机推荐

  1. js中‘!.’是什么意思

  2. Vue如何指定不编译的文件夹和favicon.ico

    这篇文章主要介绍了Vue如何指定不编译的文件夹和favicon.ico,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  3. 基于JavaScript编写一个图片转PDF转换器

    本文为大家介绍了一个简单的 JavaScript 项目,可以将图片转换为 PDF 文件。你可以从本地选择任何一张图片,只需点击一下即可将其转换为 PDF 文件,感兴趣的可以动手尝试一下

  4. jquery点赞功能实现代码 点个赞吧!

    点赞功能很多地方都会出现,如何实现爱心点赞功能,这篇文章主要为大家详细介绍了jquery点赞功能实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  5. AngularJs上传前预览图片的实例代码

    使用AngularJs进行开发,在项目中,经常会遇到上传图片后,需在一旁预览图片内容,怎么实现这样的功能呢?今天小编给大家分享AugularJs上传前预览图片的实现代码,需要的朋友参考下吧

  6. JavaScript面向对象编程入门教程

    这篇文章主要介绍了JavaScript面向对象编程的相关概念,例如类、对象、属性、方法等面向对象的术语,并以实例讲解各种术语的使用,非常好的一篇面向对象入门教程,其它语言也可以参考哦

  7. jQuery中的通配符选择器使用总结

    通配符在控制input标签时相当好用,这里简单进行了jQuery中的通配符选择器使用总结,需要的朋友可以参考下

  8. javascript 动态调整图片尺寸实现代码

    在自己的网站上更新文章时一个比较常见的问题是:文章插图太宽,使整个网页都变形了。如果对每个插图都先进行缩放再插入的话,太麻烦了。

  9. jquery ajaxfileupload异步上传插件

    这篇文章主要为大家详细介绍了jquery ajaxfileupload异步上传插件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. React学习之受控组件与数据共享实例分析

    这篇文章主要介绍了React学习之受控组件与数据共享,结合实例形式分析了React受控组件与组件间数据共享相关原理与使用技巧,需要的朋友可以参考下

返回
顶部