Javascript 为什么改变对象的[[prototype]]会对性能造成不良影响?
来自标准和非标准的MDN文档: 无论如何实现,都强烈反对对对象的[[Prototype]]进行变异,因为在现代JavaScript实现中,它非常缓慢,并且不可避免地会减慢后续执行 使用Javascript 为什么改变对象的[[prototype]]会对性能造成不良影响?,javascript,performance,prototype,prototype-chain,Javascript,Performance,Prototype,Prototype Chain,来自标准和非标准的MDN文档: 无论如何实现,都强烈反对对对象的[[Prototype]]进行变异,因为在现代JavaScript实现中,它非常缓慢,并且不可避免地会减慢后续执行 使用Function.prototype添加属性是向javascript类添加成员函数的方法。然后如下图所示: function Foo(){} function bar(){} var foo = new Foo(); // This is bad: //foo.__proto__.bar = bar; //
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.\uuuu proto\uuuuu.bar=bar代码>坏?如果它的坏不是Foo.prototype.bar=bar代码>同样糟糕
那么为什么会出现这样的警告:它非常慢,在现代JavaScript实现中不可避免地会减慢后续执行。当然Foo.prototype.bar=bar代码>没有那么糟糕
更新也许突变意味着重新分配。见公认的答案 是的。prototype=同样糟糕,因此使用了“无论如何完成”的措辞。prototype是一个伪对象,用于在类级别扩展功能。它的动态特性会减慢脚本的执行速度。另一方面,在实例级别添加一个函数会带来更少的开销
不。两者都在做相同的事情(如foo.\uuu proto\uuu==foo.prototype
),而且都很好。他们只是在Object.getPrototypeOf(foo)
对象上创建一个bar
属性
该语句所指的是为\uuuu proto\uuu
属性本身赋值:
function Employee() {}
var fred = new Employee();
// Assign a new object to __proto__
fred.__proto__ = Object.prototype;
// Or equally:
Object.setPrototypeOf(fred, Object.prototype);
位于的警告将更详细地介绍:
根据现代JavaScript引擎优化属性访问的本质,改变对象的[[Prototype]]是一个非常缓慢的操作
他们只是说,改变一个已经存在的对象的原型链会扼杀优化。相反,您应该通过object.create()
创建一个具有不同原型链的新对象
我找不到明确的参考,但是如果我们考虑如何实现,我们可以看到这里可能会发生什么。当改变一个对象的原型链时,它的内部类型会改变——它不会像添加属性时那样简单地变成一个子类,而是完全交换。这意味着将刷新所有属性查找优化,并且需要丢弃预编译代码。或者干脆退回到非优化代码
一些值得注意的引语:
-
可写(writeable)proto(proto)是实现的一大难题(必须序列化到循环检查),它会造成各种类型的混淆危险
- :
允许脚本对几乎任何对象的原型进行变异,使得更难对脚本的行为进行推理,并使VM、JIT和分析实现更复杂、更麻烦。由于可变的_proto _uuu,类型推断出现了几个错误,并且由于这个特性(即“类型集包含可以为var/属性实现的所有可能的类型对象”和“JSFunctions具有同样是函数的类型”)而无法维护几个理想的不变量
- :
原型在创建后发生变异,其不稳定的性能不稳定,以及对代理和[[SetInheritation]]的影响
- :
我不期望通过使proto不可覆盖而获得很大的性能提升。在非优化代码中,如果原型对象(而不是它们的标识)已更改,则必须检查原型链。在优化代码的情况下,如果有人向proto写入代码,您可以退回到非优化代码。所以它不会有太大的区别,至少在V8曲轴上是这样
-
当您设置_proto _时,您不仅破坏了从Ion对该对象进行未来优化的任何机会,而且还迫使引擎爬行到所有其他类型推断(可能是关于函数返回值或属性值的信息)他们认为他们知道这个对象,并告诉他们也不要做很多假设,这涉及到进一步的去优化,可能会使现有的JIT代码失效。
在执行过程中改变一个物体的原型实际上是一个讨厌的大锤,我们必须避免错误的唯一方法是安全地运行,但是安全是缓慢的。
\uuuu proto\uuuu
/setPrototypeOf
与分配给对象原型不同。例如,当您有一个分配了成员的函数/对象时:
函数构造函数(){
if(!(此构造函数实例)){
返回新构造函数();
}
}
Constructor.data=1;
Constructor.staticMember=函数(){
返回此.data;
}
Constructor.prototype.instanceMember=函数(){
返回此.constructor.data;
}
Constructor.prototype.Constructor=构造函数;
//通过执行以下操作,您所做的几乎与分配给相同
//但实际上不一样:P
var newObj=Object.create(构造函数);//但是newObj现在是一个对象而不是一个对象
//功能就像!!!建造师!!!
//(typeof newObj==='object'!==typeof构造函数==='function'),而您
//失去实例化它的能力,“newnewobj”返回的不是构造函数,
//你有。原型,但不能使用它。
newObj=Object.create(Constructor.prototype);
//现在您可以访问newObj.instanceMember了
//但staticMember不可用。构造函数的newObj instanceof为true
//我们可以使用类似于原始构造函数的函数来保留
//功能,如自调用它newObj(),访问静态
//成员等,这在Object.create中是不可能的
var newObj=函数(){
如果(!(newObj的这个实例)){
返回新的newObj();
}
};
newObj.\uuuuu proto\uuuuuj=构造函数;
newObj.prototype.\uuuu proto\uuuu=Constructor.prototype;
newObj.data=2;
(新的newObj()).instanceMember()//2.
newObj().instanceMember()/
function Employee() {}
var fred = new Employee();
// Assign a new object to __proto__
fred.__proto__ = Object.prototype;
// Or equally:
Object.setPrototypeOf(fred, Object.prototype);
NormalClass x 71,743,432 ops/sec ±2.28% (75 runs sampled)
PrototypeEdited x 73,433,637 ops/sec ±1.44% (75 runs sampled)
PrototypeReference x 71,337,583 ops/sec ±1.91% (74 runs sampled)
const Benchmark = require('benchmark')
class NormalClass {
constructor () {
this.cat = 0
}
test () {
this.cat = 1
}
}
class PrototypeEdited {
constructor () {
this.cat = 0
}
}
PrototypeEdited.prototype.test = function () {
this.cat = 0
}
class PrototypeReference {
constructor () {
this.cat = 0
}
}
var catRef = 5
PrototypeReference.prototype.test = function () {
this.cat = catRef
}
function normalClass () {
var tmp = new NormalClass()
tmp.test()
}
function prototypeEdited () {
var tmp = new PrototypeEdited()
tmp.test()
}
function prototypeReference () {
var tmp = new PrototypeReference()
tmp.test()
}
var suite = new Benchmark.Suite()
suite.add('NormalClass', normalClass)
.add('PrototypeEdited', prototypeEdited)
.add('PrototypeReference', prototypeReference)
.on('cycle', function (event) {
console.log(String(event.target))
})
.run()