JavaScript主机对象是如何实现的?

Ale*_*ian 27 javascript browser dom

今天我在想这个,我意识到我这里没有清晰的图片.

以下是我认为是真实的一些陈述(如果我错了,请纠正我):

  • DOM是W3C指定的接口集合.
  • 在解析HTML源代码时,浏览器会创建一个DOM树,其中包含实现DOM接口的节点.
  • ECMAScript规范没有引用浏览器主机对象(DOM,BOM,HTML5 API等).
  • 实际实现DOM的方式取决于浏览器的内部结构,并且在大多数情况下可能不同.
  • 现代JS解释器使用JIT来提高代码性能并将其转换为字节码

我很好奇我打电话后幕后发生的事情document.getElementById('foo').调用是否由解释器委托给浏览器本机代码,或者浏览器是否具有所有主机对象的JS实现?你知道他们对此做了什么优化吗?

我阅读了浏览器内部的这个概述,但它没有提到任何关于这个的内容.我有空的时候会浏览Chrome和FF源码,但我想先问一下这里.:)

Nic*_*lay 8

您的所有要点都是正确的,除了:

现代JS解释器使用JIT来提高代码性能并将其转换为字节码

应该是"......并将其翻译为本机代码".SpiderMonkey(Firefox中的JS引擎)在当前的JS速度军备竞赛之前很长一段时间用作字节码解释器.

在Mozilla的JS-to-DOM桥上:

宿主对象通常用C++实现,尽管正在进行实验以在JS中实现DOM.因此,当网页调用时document.getElementById('foo'),通过其ID检索元素的实际工作是在C++方法中完成的,正如hsivonen所指出的那样.

调用底层C++实现的具体方式取决于API,也随着时间的推移而改变(请注意,我没有参与开发,因此可能错误的一些细节,这是jst一篇博文,他实际参与了创建大部分代码):

  • 在最低级别,每个JS引擎都提供API来定义宿主对象.例如,浏览器可以调用JS_DefineFunctions(如SpiderMonkey用户指南中所示),让引擎知道每当脚本调用具有指定名称的函数时,应调用提供的C回调.对于宿主对象的其他方面也是如此(例如枚举,属性getter/setter等)
  • 对于核心ECMAScript功能和一些棘手的DOM情况,JS引擎/浏览器直接使用这些API来定义主机对象及其行为,但它需要许多常见的样板代码,例如检查参数类型,将它们转换为适当的C++类型,错误处理等
  • 由于我不会进入的原因,让我们说历史上,Mozilla大量使用XPCOM来处理它的许多对象,包括大部分DOM.XPCOM的一个特性是它与JS的绑定称为XPConnect.除其他事项外,XPConnect可以在IDL接口定义(如nsIDOMDocument ;或者更确切地说其编译表示),具有指定属性的脚本暴露的对象,并且之后,当一个脚本调用getElementById,执行必要的参数检查/转换并将调用直接路由到C++方法(nsDocument::GetElementById(const nsAString& aId, nsIDOMElement** aReturn))
  • XPConnect工作的方式非常低效:它将通用函数注册为在脚本访问主机对象时执行的回调,这些通用函数在每个特定情况下动态地找出了它们需要做的事情.这篇关于quickstubs的帖子向您介绍了一个例子.
  • 上一个链接中提到的"快速存根"是一种通过交换一些代码大小来优化JS-> C++调用时间的方法:而不是总是使用知道如何进行任何类型调用的通用C++函数,专用代码是自动的在Firefox构建时生成预定义的"热门"呼叫列表.
  • 后来,JIT(当时的tracemonkey)被教导生成调用C++方法的代码,作为为JS中的"热"路径生成的本机代码的一部分.我不确定较新的JIT(jaegermonkey)在这方面是如何工作的.
  • 使用"paris绑定",对象暴露给网页JS,而不依赖于XPConnect,而是基于WebIDL(而不是XPCOM时代的IDL)生成所有必需的粘合JSClass代码.另见开发人员的帖子:jstkhuey.另请参阅如何实现面向Web的DOM?

特别是对于最后三点的细节,我很模糊,所以把它带上一粒盐.

最新的改进被列为bug 622298的依赖,但我没有密切关注它们.


hsi*_*nen 7

JS调用DOM方法,例如getElementById使JS引擎调用实现DOM的C++代码.例如,在Firefox中,呼叫最终会进入nsDocument::GetElementById(const nsAString& aId, nsIDOMElement** aReturn).

正如您所看到的,Firefox维护一个哈希表,在这种情况下,它将ids映射到C++中的元素作为优化,因此它不会遍历整个DOM树来查找id.

  • ES中的全局宿主对象如何转换为C++代码? (2认同)

air*_*tyh 6

DOM在所有主要的浏览器实现中都被实现为独立于语言的库,这意味着它与Javascript引擎位​​于不同的库中.例如,在IE中,JS引擎是在jscript.dll实现DOM时实现的mshtml.dll.Safari有Nitro(JS)和WebCore(DOM).Chrome有V8(JS)和WebCore(DOM),Firefox有SpiderMonkey/TraceMonkey(JS)和Gecko(DOM).

这意味着,只要你的JS必须访问DOM,它就必须到达DOM库 - 由于必须进行的所有编组操作,这本身就很慢.已经使用的类比是通过收费桥连接的2块土地,无论何时触摸DOM,您都必须越过桥并交叉 - 支付性能损失.

参考

  • 关于参考2,"高性能JavaScript",请参阅**[此PDF文件](http://www.javascriptworkshop.com/wp-content/uploads/pdf/books/High_%20Performance_JavaScript.pdf)**. (2认同)