最近在了解javascript引擎,在这里跟大家分享一下我学习到有关于javascript引擎的知识。
首先javascript引擎是一种为解释和执行javascript代码设计的虚拟机,javascript引擎一般包含以下这几个部分。
1、编译器,主要将源代码编译成抽象语法树,有些引擎还会将抽象语法树转换成字节码。
2、解释器,主要解释执行字节码,依赖垃圾回收机制。
3、JIT工具,将字节码或抽象语法树转换成本地代码。
4、垃圾回收器和分析工具,负责垃圾回收和收集引擎中信息,改善引擎的性能。
其次javascript是单线程语言,浏览器中一个页面在不开启新的线程下,永远都是一个线程在执行js代码。但是浏览器是多进程的。
第一,不稳定,浏览器的所有功能都在一个进程里,采用多线程方式去进行的话,如果一个线程崩毁,就会影响其他线程的正常运行,从而导致进程终止(线程同步、线程不安全、线程死锁)。
第二,不流畅,浏览器的页面渲染、javascript解释运行,插件等功能都在同一个进程进行,就会导致线程之间互相抢占资源,阻塞线程运行,从而导致整个浏览器失去响应、卡顿。
第三,不安全,多进程的好处就是容易隔离,每个进程都有自己的资源,相互不干扰,如果是单进程的话,只有出现漏洞,那么进程里面的资源就会被曝光,从而失去了安全性。(想进一步了解,关键字:浏览器安全)
1、浏览器进程(只有一个):浏览器的主进程,负责浏览器界面显示,与用户交互(如前进或者后退页面)、管理各个页面,创建和销毁其他进程、将渲染进程在内存得到Bitmap,绘制到用户界面上、网络资源的下载。
2、第三方插件进程:每种类型的插件对应一个进程,只有在使用插件时才会创建。
3、GPU进程:用于3D绘制。
4、浏览器渲染进程(浏览器内核):内部是多线程,默认一个tab页面对应一个进程,互不影响(有些进程会被合并),对页面进行渲染,执行脚本,事件处理。
图1 (参考图片)
1、GUI渲染线程
负责渲染浏览器界面,解析HTML,CSS,构建DOM树和RenderObject树,布局和绘制页面等。
当界面需要重绘或由于某种操作进行回流,该线程就会执行。
GUI渲染线程与JS引擎线程是互斥的,当JS引擎执行时,GUI线程就会被挂起,GUI更新任务会被保存在一个队列,等JS引擎空闲时立刻被执行。
2、JS引擎线程
负责解析javascript脚本,处理javascript脚本程序。JS引擎一直在等任务队列的任务到来,然后进行处理,一个浏览器渲染进程什么时候都是只有一个js线程运行js程序(基于事件驱动单线程)。
3、事件触发线程
用来控制事件循环,当js引擎执行代码(setTimeOut或者鼠标点击、ajax)时,会将对应任务添加到事件线程中,当事件符合触发条件被触发时,该线程就会将事件添加任务队列的队尾,等待js引擎处理,而任务队列里的任务只有js引擎空闲时才会去执行。
4、定时触发器线程
用来帮函数setInterval与setTimeout计时,计时完毕后,事件触发线程会添加任务队列,等带js引擎空闲时执行。W3C在HTML标准中规定,setTimeout低于4ms的时间间隔算为4ms。
5、异步http请求线程
遇到异步http请求会交给该线程去处理,如果监听到状态码变更,如果有回调函数,事件触发线程会将回调函数放到任务队列的尾部等待js引擎处理。
在运行javascript代码时,实际上要维护一组用于执行 JavaScript 代码的代理,每个代理由一组执行上下文的集合、执行上下文栈、主线程、一组可能创建用于执行 worker 的额外的线程集合、一个任务队列以及一个微任务队列构成。除了主线程(某些浏览器在多个代理之间共享的主线程)之外,其它组成部分对该代理都是唯一的。
javascript存在异步执行,通过事件循环机制(event loop)实现执行。
首先,javascript任务分为同步任务和异步任务。
1)同步任务:只有等待主线程执行完上一个任务,下一个任务才能执行,不然下一个任务就会一直等待。
2)异步任务:不用等待主线程执行完上一个任务,下一个任务可以被另一个线程执行,一旦下一个任务执行,则通知主线程结果进行下一步操作(例如执行回调函数)。
主线程在执行时,会依靠执行上下文栈(存放全局执行上下文和函数执行上下文),同步任务放在这个执行上下文栈,除此之外,还会有一个任务队列,异步任务会放在这个任务队列,一旦执行上下文栈没有任务了,主线程没有执行同步任务(空闲下来),任务队列里的异步任务就会到执行上下文栈,主线程去执行异步任务的回调函数(例如鼠标点击事件、ajax回调函数)。
参考图2,图3:
图2(参考图片)
图3(参考图片)
但是到了ES6版本,引入promise,从而有了一个新的概念microtask(微任务),在同步任务和异步任务的基础上再更加细分成任务(因为要跟微任务的名字做区分,也叫宏任务)和微任务。
下面 MDN文档 对宏任务和微任务的解释。
MDN文档的解释
总的来说,就是一段javascript代码、一个事件(例如鼠标点击事件)、setInterval、setTimeout都是一个宏任务,然后setInterval、setTimeout和事件是可以将回调函数放入到宏任务队列里,等待下一轮事件循环执行。
宏任务和微任务的执行顺序:
MDN文档的解释
先是处理完宏任务队列里的宏任务,然后再去处理微任务队列里的微任务,最后再去处理渲染和绘制操作。
参考图4:
图4(参考图片)
使用微任务应当注意:
MDN文档的解释
当还在执行微任务并且微任务队列不为空时,还可以继续向微任务队列放入微任务,但是要进行下一个事件循环,得等到微任务队列为空,所以就会容易造成无尽循环,所以使用微任务还得谨慎。
哪些javascript代码是宏任务、哪些是微任务呢?
宏任务:setTimeout、setInterval、UI事件、同步javascript代码等,由宿主(浏览器、node)发起
微任务:Promise.then、process.nextTick,由javascript引擎发起
这是我对javascript引擎的初步认识,后续还会继续更新。