JavaScript 宿主对象是如何实现的?
今天我在思考这个问题,我意识到我对此没有清晰的认识。
以下是我认为正确的一些说法(如果我错了,请纠正我):
- DOM 是 W3C 指定的接口的集合。
- 当解析 HTML 源代码时,浏览器会创建一棵 DOM 树,其中包含实现 DOM 接口的节点。
- ECMAScript 规范没有浏览器主机对象(DOM、BOM、HTML5 API 等)的引用。
- DOM 的实际实现方式取决于浏览器内部结构,并且大多数浏览器之间可能有所不同。
- 现代 JS 解释器使用 JIT 来提高代码性能并将其转换为字节码
我很好奇当我调用 document.getElementById('foo')
时幕后会发生什么。调用是否由解释器委托给浏览器本机代码,或者浏览器是否具有所有主机对象的 JS 实现?您知道他们在这方面做了什么优化吗?
我阅读了浏览器内部概述,但它没有提及任何相关内容。有时间我会看一下Chrome和FF的源码,但我想先在这里问一下。 :)
I was thinking about this today and I realized I don't have a clear picture here.
Here are some statements I think to be true (please correct me if I'm wrong):
- the DOM is a collection of interfaces specified by W3C.
- when parsing HTML source code, the browser creates a DOM tree which has nodes that implement DOM interfaces.
- the ECMAScript spec has no reference of browser host objects (DOM, BOM, HTML5 APIs etc.).
- how the DOM is actually implemented depends on browser internals and is probably different among most of them.
- modern JS interpreters use JIT to improve the code performance and translate it to bytecode
I am curious about what happens behind the scenes when I call document.getElementById('foo')
. Does the call get delegated to browser native code by the interpreter or does the browser have JS implementations of all host objects? Do you know about any optimizations they do in regard to this?
I read this overview of browser internals but it didn't mention anything about this. I will look through the Chrome and FF source when I have time, but I thought about asking here first. :)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您的所有要点都是正确的,除了:
应该是“...并将其翻译为本机代码”。 SpiderMonkey(Firefox 中的 JS 引擎)在当前 JS 速度军备竞赛之前很长一段时间都作为字节码解释器工作。
在 Mozilla 的 JS-to-DOM 桥上:
主机对象通常用 C++ 实现,尽管正在进行 的实验在 JS 中实现 DOM。因此,当网页调用
document.getElementById('foo')
时,通过 ID 检索元素的实际工作是在 C++ 方法中完成的,正如 hsivonen 指出的那样。调用底层 C++ 实现的具体方式取决于 API,并且也会随着时间的推移而改变(请注意,我没有参与开发,因此可能在一些细节上有错误,这里是
getElementById
时,执行必要的操作参数检查/转换并将调用直接路由到 C++ 方法 (nsDocument::GetElementById(const nsAString& aId, nsIDOMElement** aReturn)
)我对最后三点的细节特别模糊,所以请持保留态度。
最新的改进被列为 bug 622298 的依赖项,但我不这样做密切关注他们。
All of your bullet points are correct, except:
should be "...and translate it to native code". SpiderMonkey (the JS engine in Firefox) worked as a bytecode interpreter for a long time before the current JS speed arms race.
On Mozilla's JS-to-DOM bridge:
The host objects are typically implemented in C++, though there is an experiment underway to implement DOM in JS. So when a web page calls
document.getElementById('foo')
, the actual work of retrieving the element by its ID is done in a C++ method, as hsivonen noted.The specific way the underlying C++ implementation gets called depends on the API and also changed over time (note that I'm not involved in the development, so might be wrong about some details, here's a blog post by jst, who was actually involved in creating much of this code):
getElementById
, perform the necessary parameter checks/conversions and route the call directly to a C++ method (nsDocument::GetElementById(const nsAString& aId, nsIDOMElement** aReturn)
)I'm fuzzy on details of the three last points in particular, so take it with a grain of salt.
The most recent improvements are listed as dependencies of bug 622298, but I don't follow them closely.
JS 对 DOM 方法(例如
getElementById
)的调用会导致 JS 引擎调用实现 DOM 的 C++ 代码。例如,在 Firefox 中,调用最终在nsDocument 中结束: :GetElementById(const nsAString& aId, nsIDOMElement** aReturn)
。正如您所看到的,Firefox 维护了一个哈希表,将 id 映射到 C++ 中的元素,作为本例中的优化,因此它不会遍历整个 DOM 树来查找 id。
JS calls to DOM methods like
getElementById
cause the JS engine to call into the C++ code that implements the DOM. For example, in Firefox, the call ends up innsDocument::GetElementById(const nsAString& aId, nsIDOMElement** aReturn)
.As you can see, Firefox maintains a hashtable that maps ids to elements in C++ as an optimization in this case, so it doesn't walk the whole DOM tree looking for the id.
在所有主要浏览器实现中,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 库 - 由于必须进行所有封送处理,这本质上很慢。曾经使用过的一个类比是,两块土地通过收费桥相连,任何时候你触摸 DOM,你都必须穿过桥并返回 - 支付性能费用。
参考资料
The DOM is implemented as a language-independent library pretty much in all major browser implementations, which means it's in a different library from the Javascript engine. For example in IE, the JS engine is implemented in
jscript.dll
while the DOM is implemented inmshtml.dll
. Safari has Nitro(JS) and WebCore(DOM). Chrome has V8(JS) and WebCore(DOM), and Firefox has SpiderMonkey/TraceMonkey(JS) and Gecko(DOM).What this means is that anytime your JS has to access the DOM, it has to reach over to the DOM library - which is inherently slow because of all the marshaling that has to take place. An analogy that has been used is 2 pieces of land connected by a toll bridge, any time you touch the DOM, you must cross over the bridge and cross back - paying a performance toll.
References