发表于: 2020-01-06 23:20:42
0 1518
今日完成的事
V8底层GO/VO/AO/EC
我们的代码都是放到浏览器里面执行,浏览器执行代码都有一些底层的机制。
浏览器首先会词法解析,解析出抽象语法树,然后构建出浏览器能够执行的代码。以上过程在编译器里面执行。编译器把这些做完之后交给浏览器的渲染引擎比如谷歌的v8,引擎执行代码进行一些像什么变量提升,创建作用域与作用域链,创建闭包,创建变量对象,创建堆栈内存,创建VO/AO/GO/EC/ECStack等等操作。编译器的目的就是把开发者写的代码解析浏览器看的懂的结构,这个结构就是AST结构。
浏览器引擎执行这些代码结构需要有一个执行环境,这个执行环境叫做执行环境栈也就是栈内存,执行js代码时先创建一个执行栈,执行栈叫做ECStack。栈内存就是执行代码的,给浏览器提供执行代码的环境的。在栈内存里面在开辟出1个空间叫做执行上下文EC,某个域的代码执行都有自己的执行上下文。全局的执行上下文叫做全局执行上下文EC(G),函数执行创建的上下文叫做私有执行上下文EC(某)。创建完执行上下文,把执行上下文整体压进执行栈里面执行代码,这个过程叫做进栈,有些执行上下文执行完之后没有用了执行完会出栈。进栈和出栈就是把执行上下文压进栈执行,执行完就出栈销毁。但是有些上下文特殊情况不能出栈,那就压缩到栈的最底层,这种机制就叫做闭包。全局上下文里面会创建一个全局对象GO(global object),这个全局对象赋值给window。这就是window的由来。GO里面存放了很多内置的方法,比如math,console.log()等,这些都是内置的全局对象的属性,都是浏览器提前给加好的。栈内存除了给浏览器提供代码执行的环境以外还有一个功能,就是存储基本类型值,比如我let a =12 这个12就是存在栈内存里面的。所有的变量赋值都是分为3步,第一步创建变量,如果变量已经存在就不会在创建了,第二步创建基本数据类型值,直接放到站内存储,如果有的话也不创建。第三步让变量和值关联起来。创建变量叫做声明(declare),让变量和值关联起来(赋值)叫做定义,(defined)如果声明未定义就叫做undefine。如果创建的事引用值,因为引用值是复杂的解构,所以特殊处理,他会开辟一个存储对象的键值对或者存储函数中代码的内存空间,这就叫做堆内存。所有的堆内存都有一个可被查找的16进制地址,第三步关联的时候把引用地址给了变量。
null的作用:让一个变量指向一个空的指针,取消对之前的某一个堆的引用,null没有开辟内存。我们声明的变量a会放在变量对象vo(Varibale Object)里面,如果a是在函数里面创建的那么a会放到活动对象AO(Activation Object )里面。AO与VO作用是一样的没什么区别,只不过函数里面声明的叫做AO,全局下的叫做VO,可以把AO理解为VO的一支。
总结一下函数运行,首先创建一个执行环境栈ECStack,然后在创建一个全局执行上下文EC(G),全局执行上下文里面创建一个全局对象GO里面包含各种内置方法,window指向GO。创建一个变量对象VO,里面包含了我们创建的函数名,变量,然后创建值,如果是基本数据类型的值放到执行栈里面,引用数据类型的值开辟一个对内存,然后进栈执行代码,执行完出栈或者压缩到底层。
函数的创建于运行以及重新理解作用域与作用域链
函数创建的时候2个步骤,首先创建一个堆内存用于存放函数里面的代码,第二步然后初始化函数作用域链。
这里重新解释下作用域与作用域链,函数的作用域等于他所在的上下文EC中的 变量对象(VO或者AO)举个例子
我们创建了这个名字叫做fn的函数,首先把fn这个函数名放到活动对象vo中,然后开辟一个堆内存,把console.log(y),console.log(this)这两行代码放入开辟的堆内存中,然后初始化作用域,注意这个初始化作用域。因为fn是在全局下创建的,所以fn的作用域是全局变量对象VO。假如fn是在一个函数里面创建的
那么这个fn的作用域就是op的活动对象AO,一个函数的作用域等于 所在上下文EC中的变量对象AO或者VO。所以我们以前有一种说法找一个变量先在自己的作用域里面找,自己的作用域没有再到上级作用域里面寻找,这句话是错的。举个例子
在全局下声明一个变量obj,声明一个函数fn,此时obj和fn都放在变量对象VO里面,fn是在全局下创建的所以fn的作用域是vo,那么问题来了,console.log(obj)按照以前的说法,先在自己的作用域找,自己的作用域找不到再到上级作用域找,但是fn的作用域vo里面是有obj的,所以这句话是错的。原因就是我们以前把上下文当做作用域了。其实作用域是指这个函数所在的空间,比如你在一所房子里,这个房子就是你的作用域。所以这句话应该改成先在自己的VO/AO里面找,如果没找到再到自己的作用域里面找,初始化作用域是[[scope]]。
函数执行过程
当函数执行的时候会首先创建一个执行上下文EC(j),然后初始化this指向,然后初始化作用域链,初始化作用域链叫做scopeChain,
[scope]:VO(G) scopeChain:<AO(fn),fn[[scope]]>]>
创建AO变量对象用来存储变量,创建angular,然后把上下文压进栈里执行。执行完如果全局上下文还没有执行完继续执行全局上下文。
没每一次函数执行都会创建一个新的执行上下文,函数在哪创建的他的作用域就是谁的VO/AO,如果一个上下文里面的东西被外面占用了那么这个上下文就不会销毁
评论