在聊我们今天主角V8之前,我感觉有必要简单的把浏览器的发展史描述一下。「以史为镜,可以知兴替」
提到浏览器,Berners-Lee[1]是一个不得不提的人。Berners-Lee是W3C组织的理事,他在1990年发明了世界上第一个浏览器WorldWideWeb(后改名为Nexus)。
如果大家想试试第一款浏览器,那就体验一下哇[2] (自备🪜)
1993年,浏览器Mosaic[3]诞生,这就是后来的网景(Netscape)浏览器。在当时,网景浏览器大受欢迎,占据了绝大多数的市场份额。
受Mosaic浏览器的影响,微软于1995年推出了Internet Explorer(IE)浏览器,自此第一次浏览器大战正式打响。收益于Windows操作系统,IE逐渐取代了网景浏览器的领导地位。第一次浏览器大战以IE获胜收场。
处于低谷的网景公司于1998年成立了Mozilla基金会,在该基金会推动下,网景公司主导开发FireFox 浏览器。2004年发布1.0版本,拉开了第二次浏览器大战的序幕。
在FireFox浏览器发布1.0版本的前一年(2003年),苹果发布了Safari浏览器。并于2005年,苹果公司发起了一个新的开源项目 Webkit[4](自备🪜)(它是Safari浏览器的内核)。
2008年,Google公司以苹果开源项目Webkit作为内核,创建了一个新的项目 Chromium[5] (自备🪜)。在Chromium项目的基础上,Google发布了自己的浏览器Chrome。「Chromium本身就是一个浏览器」,而不是Chrome浏览器内核,Chrome浏览器一般选择Chromium的稳定版本作为它的基础。Chromium是开源试验场。
自此,桌面浏览器形成了三足鼎立的局面 (IE/FireFox/Chrome)。
而随着时间的推移,Chrome浏览器在桌面领域一骑绝尘。将原来的对手抛到了脑后。根据statcounter[6]的最新统计,Chrome浏览器已经占据了半壁江山。
❝浏览器大战,其实就是渲染引擎之争 ❞
Chrome/Safari的渲染引擎,其实是一脉相承的。都是基于Webkit直接开发或者衍生出来的。
用了一小段八股文描述了一下,浏览器的发展历史和现在浏览器的市场占有情况。发现Chrome以绝对的优势在浏览器市场称雄称霸。本着「打不过,那就加入它」的不抵抗方针,我们后面所有的文章,都用Chrome来讲解和实验。
时间不早了,我们干点正事哇。
V8是谷歌用C++编写的开源高性能JavaScript
和WebAssembly
引擎。它被用于Chrome和Node.js等。
(针对JS的介绍,可以参考之前写的JS篇之数据类型那些事儿[7],并且后期我们也会有针对该技术的介绍和分析。)
简单的说就是:
❝v8是「JS虚拟机」的一种 (除了V8,还有其他类型的JS虚拟机。 例如Safari中的JavaScriptCore,FireFox中的TraceMonkey等) ❞
在CPU是如何运行程序的文章中介绍,程序(高级语言)如果被CPU识别和执行,就需要进行「转换」。而这个转换操作又根据语言特性分为:1. 解释执行 2. 编译执行。昨天我们通过对一段C代码,进行分析、执行,了解了编译执行的过程。而针对解释执行,却没有举例说明。今天,让我们把这个坑给填上。
把 V8 看成是一个虚构出来的计算机,也称为「虚拟机」。虚拟机通过模拟实际计算机的各种功能来实现代码的执行。如模拟实际计算机的 CPU、堆栈、寄存器等, 并且还有属于它自己的一套「指令系统」。
可以简单的把JS虚拟机理解成一个「翻译」程序: 将人类能够理解的编程语言 JS,翻译成机器能够理解的机器语言。
需要准备执行 JS 时所需要的一些基础环境
❝从图中得出一个结论: 执行JS代码核心流程 1. 先编译 2. 后执行 ❞
通过V8将js转换为字节码然后经过解释器执行输出结果的方式执行JS,有一个弊端就是,如果在浏览器中再次打开相同的页面,当页面中的 JavaScript 文件没有被修改,再次编译之后的二进制代码也会保持不变,意味着编译这一步「浪费了 CPU 资源」。
为了,更好的利用CPU资源,V8采用「JIT」(Just In Time)技术提升效率:而是混合编译执行和解释执行这两种手段。
❝1.解释执行的启动速度快,但是执行时的速度慢 2.编译执行的启动速度慢,但是执行时的速度快 ❞
为了能够实现编译执行,V8又引入了TurboFan(优化编译器),并且在解释执行字节码的过程中,如果发现了某一段代码会被「重复多次执行」,监控机器人就会将这段代码标记为热点代码。
当某段代码被标记为热点代码后,V8 就会将这段字节码丢给优化编译器(TurboFan),优化编译器会在后台将字节码编译为「二进制代码」,然后再对编译后的二进制代码执行优化操作,优化后的二进制机器代码的执行效率会得到大幅提升。
同时,由于JS是动态语言, 对象的结构和属性是可以在运行时任意修改的,经过优化编译器优化过的代码只能针对某种固定的结构。一旦在执行过程中,对象的结构被动态修改了, 优化之后的代码势必会变成无效的代码,优化编译器就需要执行「反优化操作」,经过反优化的代码,下次执行时就会回退到解释器解释执行。
参考资料:
[1]
Berners-Lee: https://baike.baidu.com/item/%E8%92%82%E5%A7%86%C2%B7%E4%BC%AF%E7%BA%B3%E6%96%AF%C2%B7%E6%9D%8E/8868412?fr=aladdin
[2]
体验一下哇: https://worldwideweb.cern.ch/browser/#https://worldwideweb.cern.ch/browser/default.html
[3]
Mosaic: https://baike.baidu.com/item/Mosaic%E6%B5%8F%E8%A7%88%E5%99%A8/9963015?fr=aladdin
[4]
Webkit: https://webkit.org/
[5]
Chromium: www.chromium.org/
[6]
statcounter: http://gsa.statcounter.com/browser-market-share/desktop/worldwide
[7]
JS篇之数据类型那些事儿: https://mp.weixin.qq.com/s/v-nqPc22fw6FIPWREOCGpQ