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 the Object.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]] using Object.create().

Try it

语法

¥Syntax

js
Object.setPrototypeOf(obj, prototype)

参数

¥Parameters

obj

要设置其原型的对象。

prototype

对象的新原型(对象或 null)。

返回值

¥Return value

指定的对象。

¥The specified object.

例外情况

¥Exceptions

TypeError

有下列情况之一的,抛出:

  • obj 参数为 undefinednull
  • obj 参数是 non-extensible,或者是 不可变原型外来对象,例如 Object.prototypewindow。但是,如果新原型与 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 作为原始值。如果 prototypeobj 的原型值相同,则直接返回 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。在浏览器中,windowlocation 是另外两个非常常见的示例。

¥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.

js
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`

示例

¥Examples

使用 Object.setPrototypeOf() 的伪经典继承

¥Pseudoclassical inheritance using Object.setPrototypeOf()

JS 中使用类进行继承。

¥Inheritance in JS using classes.

js
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:

js
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 of extends due to performance and readability reasons.

js
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

¥Specifications

浏览器兼容性

BCD tables only load in the browser

¥Browser compatibility

也可以看看