前言
- 细阅此文章大概需要 左右
- 本篇中讲述了:
-
- JS代码的运行环境
-
- ECS(Execution Context Stack)执行环境栈
-
- EC(Execution Context)执行上下文(词法作用域)
-
- 全局执行上下文
-
- 私有执行上下文
-
- EC(Execution Context)执行上下文(词法作用域)
-
- 闭包
-
- VO(Variable Object)变量对象
-
- VO(G)全局变量对象
-
- 带VAR和不带VAR的区别
-
- AO(Active Object)私有变量对象
-
- GO(Global Object)全局对象
-
- VO(G)【全局变量对象】和GO【全局对象】的关系
-
- VO(Variable Object)变量对象
-
- 如果有任何问题都可以留言给我,我看到了就会回复,如果我解决不了也可以一起探讨、学习。如果认为有任何错误都还请您不吝赐教,帮我指正,在下万分感谢。希望今后能和大家共同学习、进步。
- 下一篇会尽快更新,已经写好的文章也会在今后随着理解加深或者加入一些图解而断断续续的进行修改。
- 如果觉得这篇文章对您有帮助,还请点个赞支持一下,谢谢大家!
- 欢迎转载,注明出处即可。
Js代码在浏览器运行时的底层机制
我们编写的JS代码机器是不认识的,是需要一个环境来编译成机器码来执行的,对于JavaScript来说,这个环境可以是
-
- 浏览器(引擎)
-
- node(基于v8引擎来渲染js)
-
- webview(v8引擎)
(EC ECSTACK VO AO GO)
ECS(Execution Context Stack)执行环境栈
- 实质: 栈内存(从内存当中分配出来的一块内存),遵守先进后出原则。
- 作用: 执行环境栈用来存储执行代码期间创建的所有(EC)执行上下文。并确保程序能够按照正确的顺序被执行
- 创建时间: 在首次运行js代码时创建
EC(Execution Context)执行上下文(词法作用域)
- 实质: 代码执行所在的执行上下文,或者代码执行所处的作用域,实质上是执行环境栈中的一块栈内存。
- 作用: 在JS代码执行中,为了区分全局和函数执行所处的不同的作用域(目的是为了区分每个执行上下文中代码的独立性)
- 执行上下文又分为
- EC(G): global全局执行上下文
- EC(xx):xx函数私有执行上下文
-
EC(G):global全局执行上下文:
- 当打开页面时,首次加载一个Js文件或者运行一段Js代码,则会在【全局代码执行之前】【先创建一个全局的执行上下文EC(G)】,并将这个全局执行上下文压入【执行环境栈】中。
- 而在这个js运行的生命周期中,【EC(G)将只创建一次,只有一个】,只有页面关闭时才会释放掉。
- 刷新页面时,会把之前的上下文全都释放调,然后创建全新的上下文。
-
EC(xx):xx函数私有执行上下文:
- 在全局执行上下文创建并压入执行环境栈中之后,运行代码的过程当中,【每当有函数被调用,都会创建一个新的私有函数执行上下文并压入栈中,这个私有上下文可以保护里面的私有变量和外界互不干扰
闭包的保护机制
】 - 【函数执行上下文可存在无数个】
- 【即使是同一个函数但每一次被调用,都会创建一个新的私有上下文。】
- 每当一个函数执行完毕,则这个函数的私有执行上下文也将从栈中弹出,等到所有函数都运行完毕,要关闭页面的时候,全局上下文也将出栈释放,程序运行结束。
- 只要当前上下文中的某些内容,被当前上下文以外的东西占用,那么当前上下文是不能被释放的(上下文当中存储的变量等信息也保存下来了)
【闭包的保存机制】
- 在全局执行上下文创建并压入执行环境栈中之后,运行代码的过程当中,【每当有函数被调用,都会创建一个新的私有函数执行上下文并压入栈中,这个私有上下文可以保护里面的私有变量和外界互不干扰
-
闭包
- 闭包是一种机制
- 闭包的作用:保护/保存
- 它是函数运行时产生的机制,函数执行会在执行环境栈【ECStack】中形成一个全新的私有上下文【EC(私有)】并且在私有上下文中声明新的作用域【scope】和作用域链【scope-chain】,可以保护里面的私有变量【Ao】和外界【Vo】互不干扰
【保护机制】
- 且若私有上下文中的某些内容,被当前上下文以外的东西占用,那么当前上下文是不能被出栈释放的(这样私有变量及它的值等也不会被释放掉)
【保存机制】
- 大量应用闭包一定会导致内存消耗,但是闭包的保护和保存作用,在真实开发中我们还是需要使用,所以要合理使用闭包
VO(Variable Object)变量对象
- 作用: 是一个用来保存 【当前上下文】 中 【所有变量】 的对象(存储空间),这个对象(存储空间)【被创建在当前上下文中。】
- 在不同的上下文中,有不同的表现和叫法,但都是变量对象
VO(G)全局变量对象:
- 【对于VAR创建的变量来说】 在【全局执行上下文EC(G)中】,全局变量对象 VO(G) 的具体表现就是【全局对象(GO)】,因为【所有VAR创建的全局变量】其实都会映射给【全局对象(GO)】一份,作为其属性,【而变量对象 VO 的作用是要保存当前上下文中的所有变量,所以此时的变量对象 VO 实际上是指向的全局对象】。【所以会映射给window一份】,则全局对象(GO)就是 window,
- 【这里理解为VAR创建对象时VO(G)和全局对象有映射关系】
- 【对于LET/CONST创建的变量来说】,只是在VO(G)中创建了一个变量,并不会映射给全局一份。
-
带VAR和不带VAR的区别
- 带VAR相当于给VO(G)添加一个变量,同时映射给window【GO】设置一个属性
- 不带VAR相当于只给window【GO】设置一个属性
AO(Active Object)私有变量对象
- 叫法不同,实质相同。
- 而在【函数执行上下文EC(XX)中】,因为【私有变量对象 AO(xx)】 是要保存当前私有上下文中所有的变量,而一个函数内的变量包括:形参变量、局部变量、自身函数对象引用变量、arguments、this。 为了保存这些变量,所以特意创建了一个对象,称它为【活动对象(私有变量对象)AO(xx)】,函数内所需的变量就都保存在 【私有变量对象AO(xx)】 中,所以在函数执行上下文中,变量对象 VO 的具体表现也就是【私有变量对象AO(xx)】。
GO(Global Object)全局对象
- 与VO和AO没有什么太大关系,是预定义的对象,【浏览器把所有后期需要供JS调取使用的属性和方法(内置),都放置在了GO当中。并且在全局当中创建一个叫做window的变量指向它。】通过使用全局对象,可以访问所有其他所有预定义的对象、函数和属性。【window.什么都是去GO当中找。】
VO(G)【全局变量对象】和GO【全局对象】的关系
- 两者之间存在映射机制【创建一个全局变量,也相当于给GO设置一个属性】 单向的!!!!不存在给GO设置一个属性会映射给Vo(G)的情况,【不包括基于let/const创建的变量】
- 直接输出window.a也不会报错,因为是成员访问,若不存在a,则会返回undefined