JS高级—01—v8引擎原理
总结:
浏览器内核有两个引擎,
一个是webcore,负责页面的渲染;
一个js core, 也就是我们常说的v8引擎,负责页面里js代码的解析;
一、浏览器渲染过程
浏览器输入url后,从服务器获取到html,然后需要scrpt标签和link标签就会去加载;
浏览器内核里的html解析器(蓝色方框)会将html里的标签生成一个dom树
css解析器会生成css规则,然后结合在一起,再使用layout布局引擎将他们在不同浏览器下排版,然后再使用painting描绘引擎生成页面;
我们也可以看到一些不一样的,微信小程序中html和js解析是在两个不同的线程环境下不同的线程执行的,并且js无法控制dom则不会和渲染层dom产生资源竞争,则html的解析和js解析是不互斥的。
那么为什么浏览器中html解析和js解析是互斥的?
因为html即GUI线程的渲染dom的,js线程也可以操作dom,所以同一个进程里的这个两个线程其实会有资源竞争,那么浏览器实现里就给这两个进行了互斥(可能类似java多线程的加锁机制);
二、浏览器内核和js引擎区别
浏览器内核分两部分,一部分是webcore,一部分是jscore即js引擎;
三、js引擎之v8引擎
1。大概实现原理
js引擎有很多,我们以目前用的最多的v8引擎来探讨;
js引擎遇到js代码后,
首先由js 引擎的parse器,进行词法分析(将不表达式切成一个个词条)和语法分析(分析这个表达式的是干什么的),分析完后转化为ast树;(学习ast的网址https://astexplorer.net/)
然后js引擎的Ignition器将ast树转化为字节码,为什么要转化为字节码,因为字节码可以跨平台,以后既可以在mac上的chrome运行,也可以在win上运行,类似于java的jvm,
最后js引擎的Ignition器将ast树转化为字节码时还会进行信息收集,如果某一个函数执行的次数非常多,那么会使用trubo器将其直接转化成机器码,机器码肯定比字节码的运行速度快,这种方式可以有效加快js引擎处理js的速度;
2.执行细节
js代码在js引擎的执行过程(课二20’)
JS运行步骤:
- 语法分析
- 预编译
- 解释执行
老师讲的是es3(作用域链、vo、this),这篇文章讲得应该是es5(词法环境、语法环境、this);https://www.cnblogs.com/Rivend/p/12616528.html?ivk_sa=1024320u
总结:
es3:js代码的执行分解析和执行两个阶段;
解析阶段:在堆内存中创建go对象、fo对象;
- 其中go对象里有需要用到的array、data等各种类,还是有var定义的变量但是是undefined,还有定义的函数并且指向自身;
- 其中fo对象里有parentScope、this、函数体代码(其实fo对象里还会有一个prototype属性(显式原型属性),指向原型对象)
- (ao不会创建,ao是函数执行上下文被创建时才会创建然后去执行函数代码;不然的话在如果有几百个函数以后根本都不会被调用,那么还为它们创建ao对象就太浪费堆内存了)
解释执行阶段:
- 在栈内存中先创建调用栈,然后创建全局执行上下文并压入栈底,在全局执行上下文中的vo就是go;
- 然后执行代码,遇到调用函数时,创建函数执行上下文,函数上下文的this在这个时候指向调用它的父级,函数上下文的vo指向ao,函数上下文的作用域链就是由当前vo和fo里的父级parentScope组成,函数上下文的代码体就是fo里的代码体;
es5:js代码的执行分解析和执行两个阶段;
解析阶段:在堆内存中创建全局执行上下文,包含了this、词法环境、变量环境;
词法环境:
- 环境记录:1.记录let和const定义的变量但是是uniniitioned 2. 记录函数名并且指向自己
- 外部环境记录:由于是全局,所以是null,如果是函数则为调用的父级,类似于es3的作用域链;
变量环境:
- 环境记录:1.var定义的全局变量但是是undefined;
- 外部环境记录:由于是全局,所以是null,如果是函数则为调用的父级,类似于es3的作用域链;
执行阶段:同es3;
第一步创建阶段:
js引擎会在堆内存中创建一个go(globalObject)对象,go对象里会有date、array、string等类,会有用var定义的变量(但是没有赋值为undefined),还会有函数名并且函数名已经指向了要创建的fo对象;
因为有了函数,所以会在堆内存中新创建一个对象fo(functionObject),fo里面会有父极作用域[[scope]],和函数体也就是函数代码;
第二部执行阶段:
js引擎会在栈内存中创建一个ecs,然后再创建一个gec(gec有vo和scopeChain作用域链和主代码,vo指向go),将主代码放到gec中,现在ecs开始执行gec的主代码;
执行到变量的时候,会给赋值;
执行函数的时候,先创建一个ao,然后会创建一个fec放到ecs调用栈中,fec的vo指向ao,然后也有vo和scopeChain和主代码(对应着fo和父极作用域和fo函数体),然后开始执行这个fec的函数体,fec里有变量的时候会调用ao的,ao没有就沿着scopeChain往父极作用域找,但是函数调用结束后会释放ao,再在ecs调用栈中释放fec;
gec放到js引擎中的ecs执行;
面试题:
a是undifined不是100;
虽然有return,但是a仍然被添加到了函数的ao中;
a是undefined,b是10;
注意:这只是面试可能会问题,实际开发我们一定要避免写这种代码,b= 10会被添加到go的;
除了全局作用域、块级作用域、函数作用域外,一种新的作用域,在函数的参数有默认值时会有一种新的参数作用域;但是很少用到,了解即可(课程20‘)
ecma规范就是这么定义的: