从MDN文档获取标准setPrototypeOf功能以及非标准属性__proto__
setPrototypeOf
__proto__
强烈建议不要更改对象的[[Prototype]],因为它非常慢且不可避免地会减慢现代JavaScript实现中的后续执行,无论如何实现。
使用Function.prototype添加属性是 在 添加成员函数JavaScript类的方式。然后如下图所示:
Function.prototype
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;那么坏?
foo.__proto__.bar = bar;
Foo.prototype.bar = bar;
那么为什么会这样警告:它非常慢并且不可避免地会减慢现代JavaScript实现中的后续执行 。当然Foo.prototype.bar =bar;还不错。
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)对象上创建属性。
foo.__proto__===Foo.prototype
bar
Object.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该页面上的警告更加详细:
Object.prototype
根据 现代JavaScript引擎 如何 优化属性访问 的性质,使对象的[[Prototype]]突变是非常缓慢的操作
他们只是简单地指出, 更改 现有对象 的原型链会 终止优化 。相反,您应该通过创建具有不同原型链的新对象Object.create()。
Object.create()
我找不到明确的引用,但是如果我们考虑如何实现V8的隐藏类,则可以看到此处可能发生的情况。更改对象的原型链时,其内部类型会更改- 它不会像添加属性时那样简单地成为子类,而是会被完全交换。这意味着将刷新所有属性查找优化,并且将需要丢弃预编译的代码。或者,它只是退回到未优化的代码。
一些引人注目的报价:
可写的__proto__难以实施(必须序列化以进行循环检查),并且会产生各种类型混淆的危害。
允许脚本改变几乎所有对象的原型,这使得推理脚本的行为变得更加困难,并使VM,JIT和分析实现变得更加复杂和错误。由于__proto__的可变性,类型推断存在多个错误,并且由于此功能而无法维护多个所需的不变式(即“类型集包含可以实现var / property的所有可能的类型对象”和“ JSFunction的类型也都是函数”) )。
创建后的原型突变,其不稳定的性能不稳定,以及对代理和[[SetInheritance]]的影响
我不希望通过使原型不可重写来获得巨大的性能提升。在未优化的代码中,您必须检查原型链,以防原型对象(而不是其标识)已更改。在优化代码的情况下,如果有人写原始协议,则可以退回到非优化代码。因此,至少在V8曲轴中,它并不会带来太大变化。
设置__proto__时,不仅会浪费该离子对该对象进行未来优化的机会,而且还会迫使引擎爬行到所有其他类型推断(有关函数返回值的信息,或属性值),以为他们知道这个对象,也不告诉他们也不要做很多假设,这涉及进一步的优化以及现有jitcode的失效。在执行过程中更改对象的原型确实是一个令人讨厌的大锤,我们唯一必须避免犯错的方法就是安全地运行它,但是安全是缓慢的。