Object.setPrototypeOf()
Object.setPrototypeOf()
静态方法将指定对象的原型(即内部 [[Prototype]]
属性)设置为另一个对象或 null
。
¥The Object.setPrototypeOf()
static method sets the prototype (i.e., the internal [[Prototype]]
property) of a specified object to another object or null
.
警告:根据现代 JavaScript 引擎优化属性访问的本质,更改对象的
[[Prototype]]
目前在每个浏览器和 JavaScript 引擎中都是非常缓慢的操作。此外,更改继承的影响是微妙且广泛的,并且不限于花费在Object.setPrototypeOf(...)
语句中的时间,而是可能扩展到有权访问[[Prototype]]
已更改的任何对象的任何代码。你可以在 JavaScript 引擎基础知识:优化原型 中阅读更多内容。¥Warning: Changing the
[[Prototype]]
of an object is, by the nature of how modern JavaScript engines optimize property accesses, currently a very slow operation in every browser and JavaScript engine. In addition, the effects of altering inheritance are subtle and far-flung, and are not limited to the time spent in theObject.setPrototypeOf(...)
statement, but may extend to any code that has access to any object whose[[Prototype]]
has been altered. You can read more in JavaScript engine fundamentals: optimizing prototypes.由于此功能是语言的一部分,因此高性能(理想情况下)实现该功能仍然是引擎开发者的负担。在引擎开发者解决此问题之前,如果你担心性能,则应避免设置对象的
[[Prototype]]
。相反,使用Object.create()
创建一个具有所需[[Prototype]]
的新对象。¥Because this feature is a part of the language, it is still the burden on engine developers to implement that feature performantly (ideally). Until engine developers address this issue, if you are concerned about performance, you should avoid setting the
[[Prototype]]
of an object. Instead, create a new object with the desired[[Prototype]]
usingObject.create()
.
Try it
语法
参数
返回值
例外情况
¥Exceptions
TypeError
-
有下列情况之一的,抛出:
obj
参数为undefined
或null
。obj
参数是 non-extensible,或者是 不可变原型外来对象,例如Object.prototype
或window
。但是,如果新原型与obj
的原始原型值相同,则不会抛出该错误。prototype
参数不是对象或null
。
描述
¥Description
Object.setPrototypeOf()
通常被认为是设置对象原型的正确方法。你应该始终使用它来支持已弃用的 Object.prototype.__proto__
访问器。
¥Object.setPrototypeOf()
is generally considered the proper way to set the prototype of an object. You should always use it in favor of the deprecated Object.prototype.__proto__
accessor.
如果 obj
参数不是对象(例如数字、字符串等),则此方法不执行任何操作 - 不会将其强制为对象或尝试设置其原型 - 并直接返回 obj
作为原始值。如果 prototype
与 obj
的原型值相同,则直接返回 obj
,即使 obj
具有不可变原型,也不会导致 TypeError
。
¥If the obj
parameter is not an object (e.g. number, string, etc.), this method does nothing — without coercing it to an object or attempting to set its prototype — and directly returns obj
as a primitive value. If prototype
is the same value as the prototype of obj
, then obj
is directly returned, without causing a TypeError
even when obj
has immutable prototype.
出于安全考虑,某些内置对象被设计为具有不可变的原型。这可以防止原型污染攻击,尤其是 代理相关的。核心语言仅将 Object.prototype
指定为不可变原型外来对象,其原型始终为 null
。在浏览器中,window
和 location
是另外两个非常常见的示例。
¥For security concerns, there are certain built-in objects that are designed to have an immutable prototype. This prevents prototype pollution attacks, especially proxy-related ones. The core language only specifies Object.prototype
as an immutable prototype exotic object, whose prototype is always null
. In browsers, window
and location
are two other very common examples.
Object.isExtensible(Object.prototype); // true; you can add more properties
Object.setPrototypeOf(Object.prototype, {}); // TypeError: Immutable prototype object '#<Object>' cannot have their prototype set
Object.setPrototypeOf(Object.prototype, null); // No error; the prototype of `Object.prototype` is already `null`
示例
使用 Object.setPrototypeOf() 的伪经典继承
¥Pseudoclassical inheritance using Object.setPrototypeOf()
JS 中使用类进行继承。
¥Inheritance in JS using classes.
class Human {}
class SuperHero extends Human {}
const superMan = new SuperHero();
但是,如果我们想实现子类而不使用 class
,我们可以这样做:
¥However, if we want to implement subclasses without using class
, we can do the following:
function Human(name, level) {
this.name = name;
this.level = level;
}
function SuperHero(name, level) {
Human.call(this, name, level);
}
Object.setPrototypeOf(SuperHero.prototype, Human.prototype);
// Set the `[[Prototype]]` of `SuperHero.prototype`
// to `Human.prototype`
// To set the prototypal inheritance chain
Human.prototype.speak = function () {
return `${this.name} says hello.`;
};
SuperHero.prototype.fly = function () {
return `${this.name} is flying.`;
};
const superMan = new SuperHero("Clark Kent", 1);
console.log(superMan.fly());
console.log(superMan.speak());
如上所述,经典继承(使用类)和伪经典继承(使用构造函数的 prototype
属性)之间的相似性在 继承链 中提到。
¥The similarity between classical inheritance (with classes) and pseudoclassical inheritance (with constructors' prototype
property) as done above is mentioned in Inheritance chains.
由于函数构造函数的 prototype
属性是可写的,因此你可以将其重新分配给使用 Object.create()
创建的新对象,以实现相同的继承链。使用 create()
时需要注意一些注意事项,例如记住重新添加 constructor
属性。
¥Since function constructors' prototype
property is writable, you can reassign it to a new object created with Object.create()
to achieve the same inheritance chain as well. There are caveats to watch out when using create()
, such as remembering to re-add the constructor
property.
在下面的示例中,也使用了类,SuperHero
继承自 Human
,而不使用 extends
,而是使用 setPrototypeOf()
。
¥In the example below, which also uses classes, SuperHero
is made to inherit from Human
without using extends
by using setPrototypeOf()
instead.
警告:由于性能和可读性原因,不建议使用
setPrototypeOf()
代替extends
。¥Warning: It is not advisable to use
setPrototypeOf()
instead ofextends
due to performance and readability reasons.
class Human {}
class SuperHero {}
// Set the instance properties
Object.setPrototypeOf(SuperHero.prototype, Human.prototype);
// Hook up the static properties
Object.setPrototypeOf(SuperHero, Human);
const superMan = new SuperHero();
没有 extends
的子类化在 ES-6 子类化 中提到。
¥Subclassing without extends
is mentioned in ES-6 subclassing.
规范
Specification |
---|
ECMAScript Language Specification # sec-object.setprototypeof |
浏览器兼容性
BCD tables only load in the browser
也可以看看
¥See also