为什么改变对象的 [[prototype]] 对性能不利? [英] Why is mutating the [[prototype]] of an object bad for performance?

查看:22
本文介绍了为什么改变对象的 [[prototype]] 对性能不利?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

来自标准的 MDN 文档setPrototypeOf 函数 以及非标准的 __proto__ 属性:

From the MDN docs for the standard setPrototypeOf function as well as the non-standard __proto__ property:

强烈不鼓励改变对象的 [[Prototype]],无论这是如何实现的,因为它非常慢,并且不可避免地会减慢现代 JavaScript 实现的后续执行速度.

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 类添加成员函数的方式.然后如下图所示:

Using Function.prototype to add properties is the way to add member functions to javascript classes. Then as the following shows:

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; 一样坏?

Why is foo.__proto__.bar = bar; bad? If its bad isn't Foo.prototype.bar = bar; just as bad?

那么为什么会出现此警告:在现代 JavaScript 实现中,它非常慢并且不可避免地会减慢后续执行速度.当然 Foo.prototype.bar = bar; 并没有那么糟糕.

Then why this warning: it is very slow and unavoidably slows down subsequent execution in modern JavaScript implementations. Surely Foo.prototype.bar = bar; is not that bad.

更新 也许变异意味着重新分配.请参阅接受的答案.

Update Perhaps by mutation they meant reassignment. See accepted answer.

推荐答案

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

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

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

No. Both are doing the same thing (as foo.__proto__ === Foo.prototype), and both are fine. They're just creating a bar property on the Object.getPrototypeOf(foo) object.

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

What the statement refers to is assigning to the __proto__ property itself:

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 页面 更详细:

The warning at the Object.prototype page goes into more detail:

根据现代 JavaScript 引擎优化属性访问的方式,改变对象的 [[Prototype]] 是一个非常缓慢的操作

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

他们只是声明改变现有对象的原型链会扼杀优化.相反,您应该通过 Object.create() 使用不同的原型链创建一个新对象.

They simply state that changing the prototype chain of an already existing object kills optimisations. Instead, you're supposed to create a new object with a different prototype chain via Object.create().

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

I couldn't find an explicit reference, but if we consider how V8's hidden classes are implemented, we can see what might go on here. When changing the prototype chain of an object, its internal type changes - it does not simply become a subclass like when adding a property, but is completely swapped. It means that all property lookup optimisations are flushed, and precompiled code will need to be discarded. Or it simply falls back to non-optimized code.

一些值得注意的引述:

可写的 __proto__ 实现起来非常痛苦(必须序列化以进行循环检查),并且会产生各种类型混淆的危险.

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)说:

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

    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').

  • 杰夫沃尔登说:

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

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

  • Erik Corry(谷歌)说:

    我不期望使 proto 不可覆盖带来巨大的性能提升.在未优化的代码中,您必须检查原型链,以防原型对象(而不是它们的身份)已更改.在优化代码的情况下,如果有人写入 proto,您可以回退到未优化的代码.所以它不会有太大的不同,至少在 V8 曲轴中是这样.

    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.

  • 埃里克浮士德(Mozilla)说

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

    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.

  • 这篇关于为什么改变对象的 [[prototype]] 对性能不利?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆