首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >为什么改变对象的[[prototype]]不利于性能?

为什么改变对象的[[prototype]]不利于性能?
EN

Stack Overflow用户
提问于 2014-05-22 21:10:42
回答 4查看 34.7K关注 0票数 61

来自标准setPrototypeOf function和非标准 __proto__ property的MDN文档

强烈反对

改变对象的[原型],无论这是如何实现的,因为它非常慢,并且在现代JavaScript实现中不可避免地会减慢后续的执行速度。

使用Function.prototype添加属性是将成员函数添加到javascript类的方法。然后,如下所示:

代码语言: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;并没有那么糟糕。

更新,也许他们所说的突变意味着重新分配。请参阅接受的答案。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2014-05-22 22:05:45

//这是错误的: //foo.__proto__.bar = bar;//但这是可以的Foo.prototype.bar = bar;

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

该语句所指的是对__proto__属性本身进行赋值:

代码语言:javascript
复制
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上的警告涉及到更多细节:

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

他们简单地说,改变一个已经存在的对象的原型链会扼杀优化。相反,您应该通过Object.create()创建一个具有不同原型链的新对象。

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

一些值得注意的引用:

可写的__proto__实现起来非常痛苦(必须序列化才能进行周期检查),并且会产生各种各样的类型混淆风险。

允许脚本改变几乎任何对象的原型都会使脚本的行为更难推理,并使VM、JIT和分析实现更加复杂和麻烦。由于可变类型,类型推断已经有了几个functions').,并且由于这个特性而无法维护几个所需的不变量(即,“类型集包含可以为__proto__ /属性实现的所有可能的类型对象”和“JSFunctions具有也是可变的类型”

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

我不期望通过使proto不可重写而获得很大的性能收益。在未优化的代码中,您必须检查原型链,以防原型对象(而不是其标识)发生更改。在优化代码的情况下,如果有人写入proto,您可以回退到非优化代码。因此,至少在V8-Crankshaft.中,这不会有太大不同

当您设置__proto__时,您不仅会破坏Ion对该对象进行未来优化的任何机会,而且还会强制引擎爬行到所有其他类型推断部分(可能是关于函数返回值的信息,或者属性值),它们认为它们知道这个对象,并告诉它们不要做太多的假设,这涉及到进一步的反优化,甚至可能使现有jitcode无效。

在执行过程中更改对象的原型真的是一把讨厌的大锤,我们避免出错的唯一方法就是安全,但安全是很慢的。--

票数 62
EN

Stack Overflow用户

发布于 2014-05-30 03:36:54

__proto__/setPrototypeOf与赋值给对象原型不同。例如,当您有一个分配了成员的函数/对象时:

代码语言:javascript
复制
function Constructor(){
    if (!(this instanceof Constructor)){
        return new Constructor();
    } 
}

Constructor.data = 1;

Constructor.staticMember = function(){
    return this.data;
}

Constructor.prototype.instanceMember = function(){
    return this.constructor.data;
}

Constructor.prototype.constructor = Constructor;

// By doing the following, you are almost doing the same as assigning to 
// __proto__, but actually not the same :P
var newObj = Object.create(Constructor);// BUT newObj is now an object and not a 
// function like !!!Constructor!!! 
// (typeof newObj === 'object' !== typeof Constructor === 'function'), and you 
// lost the ability to instantiate it, "new newObj" returns not a constructor, 
// you have .prototype but can't use it. 
newObj = Object.create(Constructor.prototype); 
// now you have access to newObj.instanceMember 
// but staticMember is not available. newObj instanceof Constructor is true

// we can use a function like the original constructor to retain 
// functionality, like self invoking it newObj(), accessing static 
// members, etc, which isn't possible with Object.create
var newObj = function(){
    if (!(this instanceof newObj)){   
        return new newObj();
    }
}; 
newObj.__proto__ = Constructor;
newObj.prototype.__proto__ = Constructor.prototype;
newObj.data = 2;

(new newObj()).instanceMember(); //2
newObj().instanceMember(); // 2
newObj.staticMember(); // 2
newObj() instanceof Constructor; // is true
Constructor.staticMember(); // 1

每个人似乎都只关注原型,而忘记了函数可以在突变后分配成员和实例化。如果不使用__proto__/setPrototypeOf,目前没有其他方法可以做到这一点。几乎没有人在使用构造函数时没有继承父构造函数的能力,而且Object.create无法提供服务。

另外,这两个Object.create调用目前在V8 (浏览器和节点)中都慢得离谱,这使得__proto__成为一个更可行的选择

票数 3
EN

Stack Overflow用户

发布于 2014-05-22 21:22:27

是的,.prototype=也同样糟糕,因此才有了“不管它是如何完成的”这句话。prototype是一个伪对象,用于在类级别扩展功能。它的动态特性减慢了脚本的执行速度。另一方面,在实例级别上添加函数所产生的开销要小得多。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/23807805

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档