JavaScript中有没有有效的方法来使用`__proto__`或`setPrototypeOf()`?

12

MDN在关于修改代码中原型的警告中提到:

改变对象的[[Prototype]]是一种非常缓慢的操作,因为现代JavaScript引擎优化属性访问的方式决定了这一点,在每个浏览器和JavaScript引擎中都是如此。改变继承的影响是微妙而广泛的,并不仅限于在Object.setPrototypeOf(...)语句中花费的时间,而可能延伸到任何访问已经被更改[[Prototype]]的对象的代码中。如果您关心性能,则应避免设置对象的[[Prototype]]。相反,使用Object.create()创建具有所需[[Prototype]]的新对象。

MDN > JavaScript > Object.setPrototypeOf()

我想知道是否有任何情况下可以修改对象的原型而不会导致级联优化效果影响程序的性能。看起来应该有这样的情况。例如,如果只在对象创建后立即修改原型(并且在任何其他东西使用之前),但我想这非常依赖于引擎。

那么,有没有人知道修改对象原型的有效方法?

编辑:我提出这个问题的动机是想创建实际继承自Function的对象。而我所知道的唯一方法就是修改函数的原型。请参阅Raynos在这里的答案底部:javascript类继承自Function类


您可以使用 Object.assign(Object.create(obj), { obj2, obj3, obj4 }) 来创建一个带有obj原型的新对象。我相信这个方法来自Eric Elliot的一本书。它能够获得更好的内存占用率。我希望能给您提供更好的定义,但它与Object.create()不会实例化一个新对象有关,因此比其他获取原型的方法更快。尝试搜索类似于“Object.create内存性能”的内容,我认为您可能会找到很多好的资料。 - agm1984
4
任何问题包括“效率”和“性能”这些术语,都应该包含用于比较不同代码模式的基准;这些基准应该来自从中获得的代码;以及不同浏览器下代码的结果。否则,这种询问就是基于纯粹的猜测,而一个明确的答案是可以肯定回答的。 - guest271314
1
顺便提一下,有一种方法可以创建继承自“Function”的对象而不设置原型,但仍然设置原型更加方便。是的,“如果你只在对象创建后立即修改原型”确实应该是这个规则的一个(或者说唯一的)例外,但我无法证明它。 - Bergi
1
请参阅[为什么改变对象的[[prototype]]对性能有害?](https://dev59.com/emAg5IYBdhLWcg3wQpBX)以获取详细信息。 - Bergi
1个回答

3
这个问题的动机来自于希望创建实际继承自Function的对象。
似乎你想做以下几件事: 1. 创建新的对象。 2. 将这些对象的原型(或__proto__属性)设置为引用Fuction.prototype,使它们从Function.prototype继承属性,例如: - .apply - .call - .bind 3. 只要在创建对象时能够正确地将其设置为所需的引用,就不需要更改任何对象的原型。

答案一:如何设置对象的原型

您可以使用Object.create()在创建时设置任何对象的原型。请注意,将任何对象的原型分配给Function.prototype 不会使该对象成为可调用函数。但是如果您仍然想将原型指定为引用Function.prototype或其他对象,则可以执行以下操作:

var obj1 = Object.create(Function.prototype);

这将会:
创建一个新的对象,obj1 将该对象的原型(或 __proto__ 属性)设置为指向 Function.prototype 或传递给它的任何对象
即,它将通过 原型链 委托失败的属性查找到 Function.prototype (这就是 JavaScript 原型的含义和 __proto__ 所指的地方:一个委托失败属性查找的位置)
这不需要使用 Object.setPrototypeOf() 来更改现有对象的原型,因为它在创建对象时分配原型。请参阅 MDN 文档 Object.create() 以获取更多示例和信息 - 这不像 setPrototypeOf() 那样带有可怕的警告。您可以使用 Object.create() 来设置任何新对象的原型。

第二个答案:函数已经是对象

话虽如此,如果你想要一个像函数一样的对象,你可以简单地创建一个函数。函数仍然是一个对象,但具有额外的函数特定属性和行为;您仍然可以像分配属性给对象一样对待它。如果你想要创建一个既可以像对象一样工作,也可以像函数一样工作的函数,或者一个既可以像函数一样工作,也可以像对象一样工作的对象,那么一个函数已经做到了这一点。你可以做下面这些事情:

var func = function() { return 'hello world'; }; // func is a function
func.property1 = 'foo'; // func is also an object
func.property2 = 'bar';

对于你的问题,最好的解决方案可能是利用函数实际上已经是具有额外功能的对象这一事实。


1
让一个不可调用的对象继承Function.prototype方法是没有意义的。而且,Object.create无法创建可调用的对象。 - Bergi
同意!话虽如此,了解如何使用Object.create()来分配新对象的原型仍然很重要,因此我认为值得包含。 - moriah

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接