为什么JS工厂/关闭比构造器/原型慢得多?

时间:2018-12-28 00:46:16

标签: v8 spidermonkey javascriptcore

很久以前,JS中的工厂/关闭量在构造函数/原型的15%范围内。如今,支持原型的差异超过8000%(原型使用大约一半的内存)。

https://jsperf.com/prototype-vs-factory-performance/4

闭包(理论上)不会创建更多对象。您拥有闭包和实例,而不是原型和实例(闭包还有另一个优势,因为您无法从中添加/删除属性)。我唯一的结论是,即使函数是基元并且是不变的(尽管函数对象不是),它们也不会被中断,从而导致指令缓存跳动。这种差异似乎出现在JS引擎之间。

有人对为什么存在如此巨大的差距有任何实际的事实吗?

1 个答案:

答案 0 :(得分:1)

  

闭包(理论上)不会创建更多对象。

这种“关闭/工厂”对象创建样式的确创建了更多对象:使用原型,原型方法的实例是共享的,而在“工厂”样式中,每个对象实例将获得所有方法的自己的副本。这是可以观察到的,因此引擎无法对其进行优化。考虑:

var x1 = createValueObject();
var x2 = createValueObject();
x1.get.my_tag = 42;
console.log(x2.get.my_tag);      // undefined
console.log(x2.get === x1.get);  // false

var y1 = new ValueObject();
var y2 = new ValueObject();
y1.get.my_tag = 123;
console.log(y2.get.my_tag);      // 123
console.log(y2.get === y1.get);  // true

我想强调的是,一般而言,使用闭包和工厂是完全可以的。这一点仅适用于创建对象的这种特定模式


  

https://jsperf.com/prototype-vs-factory-performance/4

这也是一个很好的例子:当心误导性的微基准测试!

只要在jsperf.com基准测试中看到数亿次操作/秒,那么几乎可以肯定的是,优化编译器设法消除了所有代码,并且正在测量空循环。没有实际的操作会这么快。

在这种情况下这并不奇怪:使用原型是在JavaScript中定义/创建对象的 惯用方式,并且引擎一直在投入大量精力来优化该模式的各个方面,因此现代引擎能够跟踪被调用的原型方法,最终内联(不是立即,仅在热代码中!),发现它们不会产生有用的结果,并丢弃所有无用的代码。

有了适当,谨慎的基准测试,我希望您所说的“构造函数/原型”模式仍然明显更快,但不如当前结果令人误解的那么快。


  

指令缓存崩溃

否,指令缓存与此无关。

  

很久以前,JS中的工厂/关闭量在构造函数/原型的15%范围内。

我很难相信过去十年中的任何时候都是如此。也许20年前,当一切都变得缓慢时?