浏览器在每次页面加载时都解析JavaScript吗?

200

浏览器(IE和Firefox)每次刷新页面都会解析链接的javascript文件吗?

它们可以缓存文件,因此我猜想它们不会每次尝试下载文件,但由于每个页面本质上是分开的,我期望它们拆除任何旧代码并重新解析它。

这是低效的,虽然完全可以理解,但我想知道现代浏览器是否足够聪明,以避免在站点内进行解析步骤。我考虑的情况是站点使用了一个javascript库,比如ExtJS或jQuery等。


4
我的看法是:我认为缓存解析后的JavaScript文件所带来的性能优势太小,无法成为一种有意义的优化。 - Itay Maman
2
从我的基准测试来看,这可能真的很重要。例如,jQuery 的加载时间大约为 30 毫秒(在快速桌面机器上),其中 20% 只是将代码解析为可执行表示形式,其余的则是执行它,即在这种情况下初始化 jQuery 对象。如果您使用两个或三个库并且正在移动设备上使用,则此延迟可能是相关的,因为 JavaScript 执行是阻塞的,并且页面在每个 JS 脚本加载到内存中之前基本上为空白。 - djjeck
6个回答

348
这是我能找到的详细信息。首先值得注意的是,尽管JavaScript通常被认为是解释执行并在虚拟机上运行,但现代解释器实际上并非如此,它们倾向于直接将源代码编译成机器码(除了IE)。

Chrome: V8引擎

V8有一个编译缓存。它使用源代码的哈希值来存储已编译的JavaScript,最多可存储5个垃圾回收。这意味着两个相同的源代码将共享内存中的缓存条目,无论它们是如何被包含的。当页面重新加载时,此缓存不会被清除。

来源


更新 - 2015年3月19日

Chrome团队发布了有关他们的JavaScript流媒体和缓存新技术的详细信息

  1. 脚本流媒体

脚本流媒体优化了JavaScript文件的解析。[...]

从版本41开始,Chrome会在下载开始后立即将异步和延迟脚本解析到单独的线程中。这意味着解析可以在下载完成后仅几毫秒内完成,并且导致页面加载速度提高了多达10%。

  1. 代码缓存
通常情况下,V8引擎会在每次访问页面时编译页面的JavaScript代码,将其转换为处理器可以理解的指令。一旦用户离开页面,这个编译后的代码就会被丢弃,因为编译后的代码高度依赖于编译时机器的状态和上下文。
Chrome 42引入了一种先进的技术,即存储已编译代码的本地副本,以便当用户返回页面时,可以跳过下载、解析和编译步骤。在所有页面加载中,这使得Chrome可以避免约40%的编译时间,并节省移动设备的宝贵电池寿命。

Opera : Carakan Engine

实际上,这意味着每当要编译一个脚本程序时,如果其源代码与最近编译的某个其他程序完全相同,我们将重用编译器的先前输出并完全跳过编译步骤。在典型的浏览场景中,这种缓存非常有效,因为用户经常从同一网站加载页面,例如从新闻服务加载不同的新闻文章,每个页面通常都会加载相同的(有时非常大的)脚本库。

因此,JavaScript 跨页面重新加载是被缓存的,对同一脚本的两个请求不会导致重新编译。

来源


Firefox : SpiderMonkey引擎

SpiderMonkey使用JIT编译器Nanojit作为其本地后端。可以在此处看到编译机器代码的过程。简而言之,它似乎会在脚本加载时重新编译脚本。但是,如果我们更仔细地观察Nanojit的内部结构,我们会发现高级监视器jstracer在编译过程中可以通过三个阶段进行转换,从而使Nanojit受益:

跟踪监视器的初始状态是监视。这意味着spidermonkey正在解释字节码。每次spidermonkey解释一个向后跳转的字节码时,监视器都会记录跳转目标程序计数器(PC)值已经被跳转的次数。该数字称为PC的命中数。如果特定PC的命中计数达到阈值,则将目标视为热点。
当监视器确定目标PC是热点时,它会查找片段哈希表,以查看是否有保存该目标PC的本地代码的片段。如果找到这样的片段,则转换为执行模式。否则,转换为记录模式。
这意味着对于热点代码的本地代码将被缓存。这意味着不需要重新编译。并没有明确说明这些哈希本地部分在页面刷新之间是否保留。但我认为他们是。如果有人可以找到支持此论点的证据那就太好了。

编辑: Mozilla 开发者 Boris Zbarsky 表示 Gecko 目前还没有缓存已编译的脚本。引自 这个 Stack Overflow 回答


Safari: JavaScriptCore/SquirrelFish引擎

我认为对于这个实现来说,最好的答案已经由其他人给出

我们目前不缓存字节码(或本地代码)。虽然我们考虑过这个选项,但是目前,代码生成只占JS执行时间的一个微不足道的部分(<2%),所以我们暂时不追求这个方向。

这是由Safari的首席开发人员Maciej Stachowiak编写的。所以我认为这是真实可信的。

我找不到其他信息,但你可以在这里阅读有关最新的SquirrelFish Extreme引擎速度提升的更多信息,或者如果你感到冒险,在这里浏览源代码。

IE:Chakra引擎

目前没有关于IE9的JavaScript引擎(Chakra)在这个领域的任何信息。如果有人知道,请评论。

这是相当非官方的,但对于IE旧版引擎实现,Eric Lippert(MS JScript开发人员)在博客回复此处中指出:

JScript Classic的行为类似于编译语言,因为在运行任何JScript Classic程序之前,我们完全语法检查代码,生成完整的解析树,并生成字节码。然后我们通过字节码解释器运行字节码。从这个意义上说,JScript和Java一样“编译”。区别在于,JScript不允许您保留或检查我们的专有字节码。此外,字节码比JVM字节码高级得多 - JScript Classic字节码语言几乎只是解析树的线性化,而JVM字节码显然旨在在低级堆栈机上操作。

这意味着字节码不以任何方式持久存在,因此字节码不会被缓存。

10
+1,写得很好。不过,关于Firefox,请参见这个StackOverflow问题,Mozilla开发者Boris Zbarsky解释说Gecko目前并没有这样做。 - cha0site
谢谢,我在搜索中看到了这个,但没有找到其他支持证据。我会编辑答案并加上它。 - Jivings
1
请注意,关于IE的讨论是在2003年进行的:IE9的JS引擎首次发布是在2011年的IE9中。 - gsnedders
此外,Opera不仅在重新加载时缓存JS字节码。(但生成的机器代码不会被缓存)。 - gsnedders
2
@Jivings,请将上述内容视为来源。(我是Carakan团队的成员之一。) - gsnedders
显示剩余4条评论

12

如其他答案所述,Opera可以这样做。(来源)

Firefox(SpiderMonkey引擎)不会缓存字节码。(来源)

WebKit(Safari,Konqueror)也不会缓存字节码。(来源)

我不确定IE [6/7/8]或V8(Chrome),我认为IE可能会进行某种缓存,而V8可能不会。 IE是闭源的,所以我不确定,但在V8中,缓存“编译”代码可能没有意义,因为它们直接编译成机器代码。


1
IE6-8几乎肯定不支持。IE9可能会,但我没有任何证据。编译后的JS很可能没有被缓存,因为它通常相当大。 - gsnedders
@gsnedders:我不确定IE8在技术上是否无法做到,因为它似乎也会编译成字节码(非官方但接近),所以没有技术上的理由不缓存它。IE9似乎添加了JIT以编译成本地代码。 - cha0site
2
字节码一直被IE使用...从来没有什么新的东西在IE8中。这只是因为给定一个解释器,解释器的性能比解析时间慢得多,所以它完全无关紧要。IE9有一个全新的(从头开始)JS引擎,所以两者之间没有任何关系。 - gsnedders

3
据我所知,只有Opera会缓存解析后的JavaScript。在这里可以查看“已缓存的编译程序”部分(链接)

谢谢,您也有其他浏览器族系的更多详细信息吗? - ajreal

2

0

我认为正确的答案应该是“并非总是如此”。据我所知,浏览器和服务器都在确定缓存内容方面发挥作用。如果您确实需要每次重新加载文件,则应该能够从Apache(例如)内部进行配置。当然,我想用户的浏览器可能会配置忽略该设置,但这可能不太可能。

因此,我想在大多数实际情况下,JavaScript 文件本身被缓存,但每次页面加载时都会动态重新解释。


0
浏览器肯定利用缓存,但是当页面刷新时,浏览器会重新解析JavaScript。 因为每当浏览器加载页面时,它会创建2个树 1.内容树和 2.渲染树。
这个渲染树包含有关DOM元素的可视布局的信息。所以每当一个页面被加载时,JavaScript会被解析并且由JavaScript进行任何动态更改,如定位DOM元素、显示/隐藏元素、添加/删除元素都会导致浏览器重新创建渲染树。但是像Firefox和Chrome这样的现代浏览器处理方式略有不同,它们具有增量渲染的概念,因此只有当JS进行上述动态更改时,它才会导致这些元素重新渲染和重绘。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接