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运行步骤:

  1. 语法分析
  2. 预编译
  3. 解释执行

老师讲的是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规范就是这么定义的:

 

posted @ 2022-03-31 18:03  Eric-Shen  阅读(554)  评论(0编辑  收藏  举报