为什么改变对象的[[prototype]]对性能有害?

bas*_*rat 54 javascript performance prototype prototype-chain

标准 setPrototypeOf函数的MDN文档 以及非标准属性: __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
Run Code Online (Sandbox Code Playgroud)

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

那么为什么会出现这样的警告:它非常缓慢并且不可避免地减慢了现代JavaScript实现中后续执行的速度.当然Foo.prototype.bar = bar;不是那么糟糕.

更新也许通过突变他们意味着重新分配.见接受的答案.

Ber*_*rgi 57

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

// But this is okay
Foo.prototype.bar = bar;
Run Code Online (Sandbox Code Playgroud)

不.两者都在做同样的事情(如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);
Run Code Online (Sandbox Code Playgroud)

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

根据现代JavaScript引擎优化属性访问的性质,改变对象的[[Prototype]],操作非常慢

他们只是声明改变现有对象的原型链会导致优化.相反,你应该创建一个具有不同原型链的新对象Object.create().

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

一些值得注意的引用:

  • Brendan Eich(你认识他)说

    可写的__proto__是一个巨大的痛苦(必须序列化到循环检查),它会产生各种类型混淆的危险.

  • Brian Hackett(Mozilla)说:

    允许脚本改变几乎任何对象的原型使得更难以推断脚本的行为并使VM,JIT和分析实现更复杂和更复杂.类型推断由于可变的__proto__而有几个错误,并且因为这个特性而不能维护几个理想的不变量(即'类型集包含可以为var/property实现的所有可能的类型对象'和'JSFunctions具有也是函数的类型' ).

  • 杰夫沃尔登说:

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

  • Erik Corry(谷歌)说:

    我不认为使proto不可覆盖会带来巨大的性能提升.在非优化代码中,您必须检查原型链,以防原型对象(而不是其标识)已更改.在优化代码的情况下,如果有人写入proto,您可以回退到非优化代码.所以它不会产生那么大的差别,至少在V8-Crankshaft.

  • 埃里克·福斯特(Mozilla)说

    当你设置__proto__时,你不仅会破坏你对该对象的Ion进行未来优化的任何机会,而且还会强制引擎继续爬行到所有其他类型的推断(有关函数返回值的信息,或许属性值,或许)认为他们知道这个对象,并告诉他们不要做出许多假设,这涉及进一步的去优化和可能使现有的jitcode无效.
    在执行过程中更改对象的原型实际上是一个讨厌的大锤,我们必须避免错误的唯一方法是保证安全,但安全是缓慢的.

  • object.create和__proto__之间存在显着的性能差异:http://jsperf.com/proto-vs-object-create2感谢您的时间 (2认同)