javascript - 为什么改变对象的 [[prototype]] 会降低性能?

标签 javascript performance prototype prototype-chain

来自标准的 MDN 文档 setPrototypeOf function以及非标 __proto__ property :

Mutating the [[Prototype]] of an object, no matter how this is accomplished, is strongly discouraged, because it is very slow and unavoidably slows down subsequent execution in modern JavaScript implementations.

使用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; 并没有那么糟糕。

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

最佳答案

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

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

没有。两者都在做同样的事情(如 foo.__proto__ === Foo.prototype ),并且都很好。他们只是创建一个bar属性 Object.getPrototypeOf(foo)对象。

该语句指的是分配给 __proto__属性(property)本身:

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 page处的警告更详细的内容:

Mutating the [[Prototype]] of an object is, by the nature of how modern JavaScript engines optimize property accesses, a very slow operation

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

我找不到明确的引用,但如果我们考虑如何 V8's hidden classes已实现(以及 more recent write-up ),我们可以看看这里会发生什么。当改变一个对象的原型(prototype)链时,它的内部类型就会改变——它不会像添加属性时那样简单地变成子类,而是完全交换。这意味着所有属性查找优化都将被刷新,并且预编译代码将需要被丢弃。或者它只是退回到未优化的代码。

一些值得注意的引言:

  • Brendan Eich (you know him) said

    Writable _proto_ is a giant pain to implement (must serialize to cycle-check) and it creates all sorts of type-confusion hazards.

  • Brian Hackett (Mozilla) said :

    Allowing scripts to mutate the prototype of pretty much any object makes it harder to reason about the behavior of a script and makes VM, JIT, and analysis implementation more complex and buggier. Type inference has had several bugs due to mutable _proto_ and cannot maintain several desirable invariants because of this feature (i.e. 'type sets contain all the possible type objects which can realized for a var/property' and 'JSFunctions have types which are also functions').

  • Jeff Walden said :

    Prototype mutation after creation, with its erratic performance destabilization, and the impact upon proxies and [[SetInheritance]]

  • Erik Corry (Google) said :

    I don't expect big performance gains from making proto non-overwritable. In non-optimized code you have to check the prototype chain in case the prototype objects (not their identity) have been changed. In the case of optimized code you can fall back to nonoptimized code if someone writes to proto. So it wouldn't make all that much difference, at least in V8-Crankshaft.

  • Eric Faust (Mozilla) said

    When you set _proto_, not only are you ruining any chances you may have had for future optimizations from Ion on that object, but you also force the engine to go crawling around to all the other pieces of type inference (information about function return values, or property values, perhaps) which think they know about this object and tell them not to make many assumptions either, which involves further deoptimization and perhaps invalidation of existing jitcode.
    Changing the prototype of an object in the middle of execution is really a nasty sledgehammer, and the only way we have to keep from being wrong is to play it safe, but safe is slow.

关于javascript - 为什么改变对象的 [[prototype]] 会降低性能?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46969351/

相关文章:

c++ - vector 和原始 C 风格数组之间的性能比较

JavaScript:将 child 的原型(prototype)分配给自己?

javascript - 如何检查和测试 JavaScript 应用程序泄漏了多少内存

javascript - 与 Bootstrap 下拉菜单链接时如何使用 Javascript if 语句

javascript - 如何将 onload 属性与 ng-controller 指令一起使用来执行 Controller 中定义的函数?

javascript - 更改 drillUpButton 文本的字体颜色

javascript - 为什么我尝试使用 Web Performance API 测量渲染时间总是导致负值

javascript - 为什么下面的 `constructor`指向Object?

javascript - 这些原型(prototype)声明中哪个更好,为什么?

javascript - 如何将项目拖放到外部容器?