当前位置: 首页 > 面试题库 >

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

章安易
2023-03-14
问题内容

从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;还不错。

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


问题答案:
// 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的失效。在执行过程中更改对象的原型确实是一个令人讨厌的大锤,我们唯一必须避免犯错的方法就是安全地运行它,但是安全是缓慢的。



 类似资料:
  • 在使用一个库的时候碰到了一个很奇怪的问题,有个class实例化生成的对象,假设为A 对A的属性进行修改 打印A发现,其中的a属性并没有变,但是直接打印A.a是改变了的,请问这种情况大概会是什么原因呢,是有什么知识盲区吗,目前知道a属性是不可删除的属性,但是一般对象设置configurable=false也不会出现这种情况,如果不允许修改的话应该也会报错。。 (PS:A只是为了表述简化的例子,并不是

  • 问题内容: 我正在尝试使我的元素保持原状(过渡之后)。现在,翻译后的位置是我想要的位置,但随后我的名字又回到了报价单上。我是否缺少一段代码,或者是否有一段代码使这种回弹发生? 问题答案: 原因和解决方法: CSS转换通常不能应用于具有设置的元素。虽然奇怪的是,这种情况似乎最初发生在回弹之前,但解决方案是将设置更改为下面的代码段所示。 如果元素不可变形,为什么最初会对其进行翻译? 浏览器(至少是Ch

  • 我在Pytorch中用LSTM-线性模块构建了一个分类问题(10个类)的模型。我正在训练模型,对于每个时代,我在训练集中输出损失和准确性。产出如下: 纪元:0开始 损失:2.301875352859497 会计科目:0.1138888889 时代:1开始<损失:2.2759320735931396 会计科目:0.29 时代:2开始<损失:2.2510263919830322 会计科目:0.4872

  • 问题内容: 当您在子类中重写某个方法时降低了其可见性时,为什么编译器会给出错误消息? 问题答案: 因为子类的每个实例仍然需要是基类的有效实例(请参见Liskov替换原理)。 如果子类突然失去了基类的一个属性(例如,一个公共方法),那么它将不再是基类的有效替代品。

  • 问题内容: 嗨,我有一个使用自定义格式字符串设置的简单日期格式:MMddyy 我给它解析以下值:4 1 01 我认为它不应该因为空格而解析,但是“简单日期格式”会返回日期 0001AD年4月4日 有什么想法吗? 问题答案: 这是预期的行为-您告诉DateFormat对象期望日期的6个字符的String表示形式,这就是您传递的内容。对空格进行了解析。但是,如果您使用“ 4x1x01”,则会出现错误。

  • 问题内容: 每个Java 都有方法和(以及其他变体)。我从来没有使用过这些,我怀疑很多其他人没有使用过。为什么这些是如此基本,以至于每个对象都必须拥有它们,并且拥有它们会对性能产生影响(大概在其中存储了某种状态)? 编辑 以强调该问题。如果我有一个具有100,000个元素的元素,那么每个元素都具有从扩展的这些方法。但是似乎所有这些都不大可能必须了解管理的线程。 编辑 出色而有用的答案。@Jon有一