小编典典

为什么对象的[[prototype]]进行突变会降低性能?

javascript

从MDN文档获取标准setPrototypeOf功能以及非标准属性__proto__

强烈建议不要更改对象的[[Prototype]],因为它非常慢且不可避免地会减慢现代JavaScript实现中的后续执行,无论如何实现。

使用Function.prototype添加属性是 添加成员函数JavaScript类的方式。然后如下图所示:

function Foo(){}
function bar(){}

var foo = new Foo();

// This is bad: 
//foo.__proto__.bar = bar;

// But this is okay
Foo.prototype.bar = bar;

// Both cause this to be true: 
console.log(foo.__proto__.bar == bar); // true

为什么foo.__proto__.bar = bar;不好?如果它的坏处不Foo.prototype.bar = bar;那么坏?

那么为什么会这样警告:它非常慢并且不可避免地会减慢现代JavaScript实现中的后续执行 。当然Foo.prototype.bar =bar;还不错。

更新 也许通过突变他们意味着重新分配。查看已接受的答案。


阅读 306

收藏
2020-04-25

共1个答案

小编典典

// This is bad:
//foo.__proto__.bar = bar;

// But this is okay
Foo.prototype.bar = bar;

否。两者都在做相同的事情(与foo.__proto__===Foo.prototype),都很好。他们只是barObject.getPrototypeOf(foo)对象上创建属性。

语句所指的是分配给__proto__属性本身:

function Employee() {}
var fred = new Employee();

// Assign a new object to __proto__
fred.__proto__ = Object.prototype;
// Or equally:
Object.setPrototypeOf(fred, Object.prototype);

Object.prototype该页面上的警告更加详细:

根据 现代JavaScript引擎 如何 优化属性访问 的性质,使对象的[[Prototype]]突变是非常缓慢的操作

他们只是简单地指出, 更改 现有对象 的原型链会 终止优化 。相反,您应该通过创建具有不同原型链的新对象Object.create()

我找不到明确的引用,但是如果我们考虑如何实现V8的隐藏类,则可以看到此处可能发生的情况。更改对象的原型链时,其内部类型会更改-
它不会像添加属性时那样简单地成为子类,而是会被完全交换。这意味着将刷新所有属性查找优化,并且将需要丢弃预编译的代码。或者,它只是退回到未优化的代码。

一些引人注目的报价:

可写的__proto__难以实施(必须序列化以进行循环检查),并且会产生各种类型混淆的危害。

允许脚本改变几乎所有对象的原型,这使得推理脚本的行为变得更加困难,并使VM,JIT和分析实现变得更加复杂和错误。由于__proto__的可变性,类型推断存在多个错误,并且由于此功能而无法维护多个所需的不变式(即“类型集包含可以实现var / property的所有可能的类型对象”和“ JSFunction的类型也都是函数”) )。

创建后的原型突变,其不稳定的性能不稳定,以及对代理和[[SetInheritance]]的影响

我不希望通过使原型不可重写来获得巨大的性能提升。在未优化的代码中,您必须检查原型链,以防原型对象(而不是其标识)已更改。在优化代码的情况下,如果有人写原始协议,则可以退回到非优化代码。因此,至少在V8曲轴中,它并不会带来太大变化。

设置__proto__时,不仅会浪费该离子对该对象进行未来优化的机会,而且还会迫使引擎爬行到所有其他类型推断(有关函数返回值的信息,或属性值),以为他们知道这个对象,也不告诉他们也不要做很多假设,这涉及进一步的优化以及现有jitcode的失效。在执行过程中更改对象的原型确实是一个令人讨厌的大锤,我们唯一必须避免犯错的方法就是安全地运行它,但是安全是缓慢的。

2020-04-25