很久以前,JS中的工厂/关闭量在构造函数/原型的15%范围内。如今,支持原型的差异超过8000%(原型使用大约一半的内存)。
https://jsperf.com/prototype-vs-factory-performance/4
闭包(理论上)不会创建更多对象。您拥有闭包和实例,而不是原型和实例(闭包还有另一个优势,因为您无法从中添加/删除属性)。我唯一的结论是,即使函数是基元并且是不变的(尽管函数对象不是),它们也不会被中断,从而导致指令缓存跳动。这种差异似乎出现在JS引擎之间。
有人对为什么存在如此巨大的差距有任何实际的事实吗?
答案 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
我想强调的是,一般而言,使用闭包和工厂是完全可以的。这一点仅适用于创建对象的这种特定模式。
这也是一个很好的例子:当心误导性的微基准测试!
只要在jsperf.com基准测试中看到数亿次操作/秒,那么几乎可以肯定的是,优化编译器设法消除了所有代码,并且正在测量空循环。没有实际的操作会这么快。
在这种情况下这并不奇怪:使用原型是在JavaScript中定义/创建对象的 惯用方式,并且引擎一直在投入大量精力来优化该模式的各个方面,因此现代引擎能够跟踪被调用的原型方法,最终内联(不是立即,仅在热代码中!),发现它们不会产生有用的结果,并丢弃所有无用的代码。
有了适当,谨慎的基准测试,我希望您所说的“构造函数/原型”模式仍然明显更快,但不如当前结果令人误解的那么快。
指令缓存崩溃
否,指令缓存与此无关。
很久以前,JS中的工厂/关闭量在构造函数/原型的15%范围内。
我很难相信过去十年中的任何时候都是如此。也许20年前,当一切都变得缓慢时?