Object.setPrototypeOf()
Baseline Widely available
This feature is well established and works across many devices and browser versions. It’s been available across browsers since July 2015.
Object.setPrototypeOf()
静的メソッドは、指定されたオブジェクトのプロトタイプ(つまり、内部の [[Prototype]]
プロパティ)を、別のオブジェクトまたは null
に設定します。
警告:
オブジェクトの [[Prototype]]
を変更すると、 最近の JavaScript エンジンがプロパティへのアクセスを最適化する方法の特質上、すべてのブラウザーや JavaScript エンジンで、操作がとても低速になります。さらに、プロトタイプを変更することの性能への影響は細かく広範囲にわたり、 Object.setPrototypeOf(...)
文に費やされる時間だけではなく、 [[Prototype]]
が変更されたすべてのオブジェクトへのアクセスを持つ**すべて**のコードに影響する可能性があります。詳しくは JavaScript engine fundamentals: optimizing prototypes をお読みください。
この機能は言語の一部であるため、その機能の実装による負荷は (理念上は) エンジンの開発者によります。エンジンの開発者がこの問題に対処するまでの間、性能が気になる場合は、オブジェクトの [[Prototype]]
を変更することは避けるべきです。代わりに、 Object.create()
を使用して必要な [[Prototype]]
をもつオブジェクトを生成してください。
試してみましょう
const obj = {};
const parent = { foo: "bar" };
console.log(obj.foo);
// Expected output: undefined
Object.setPrototypeOf(obj, parent);
console.log(obj.foo);
// Expected output: "bar"
構文
Object.setPrototypeOf(obj, prototype)
引数
返値
指定されたオブジェクト。
例外
TypeError
-
以下のいずれかの場合に発生します。
- 引数
obj
がundefined
またはnull
である場合。 - 引数
obj
が拡張不可能であるか、またはプロトタイプが不変のエキゾチックオブジェクト、例えばObject.prototype
やwindow
等の場合。ただし、新しいプロトタイプがobj
の元のプロトタイプと同じ値である場合は、エラーは発生しません。 - 引数
prototype
がオブジェクトまたはnull
ではない場合。
- 引数
解説
Object.setPrototypeOf()
は、一般的にオブジェクトのプロトタイプを設定するための適切な方法と考えられています。非推奨の Object.prototype.__proto__
アクセサーの代わりに、常にこれを使用するべきでしょう。
もし引数 obj
がオブジェクト(例えば、数値、文字列など)でない場合、このメソッドは何もせず、オブジェクトに変換したり、プロトタイプを設定することなく、直接 obj
をプリミティブ値として返します。もし prototype
が obj
のプロトタイプと同じ値であれば、 obj
のプロトタイプが不変であっても TypeError
は発生せずに obj
が直接返されます。
セキュリティの観点から、プロトタイプが不変であるように設計された組み込みオブジェクトがあります。これにより、プロトタイプ汚染攻撃、特にブロキシー関連の攻撃を防ぐことができます。コア言語では、不変のプロトタイプを持つエキゾチックオブジェクトとして Object.prototype
のみを指定しており、そのプロトタイプは常に null
です。ブラウザーでは、 window
や location
の 2 つがとても一般的な例です。
Object.isExtensible(Object.prototype); // true。プロパティを追加できる
Object.setPrototypeOf(Object.prototype, {}); // TypeError: 不変のプロトタイプオブジェクト '#<Object>' にプロトタイプを設定することはできない
Object.setPrototypeOf(Object.prototype, null); // エラーなし。 `Object.prototype` のプロパティがすでに `null` であるため
例
Object.setPrototypeOf() を使った擬似クラス継承
クラスを使用した JS の継承です。
class Human {}
class SuperHero extends Human {}
const superMan = new SuperHero();
しかし、 class
を使わずにサブクラスを実装したい場合は、次のようにします。
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);
// `SuperHero.prototype` の `[[Prototype]]` を `Human.prototype` に設定
// プロトタイプのインスタンスチェーンを設定するため
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
プロパティによる継承)の類似性については、継承チェーンで述べられています。
関数コンストラクターの prototype
プロパティは書き込み可能なので、 Object.create()
で作成した新しいオブジェクトに再割り当てすることで、同じ継承連鎖を実現できます。 create()
を使用する際には、 constructor
プロパティを再度追加することを忘れないようにするなどの注意点があります。
以下の例でもクラスを使用していますが、 SuperHero
は extends
を使用せずに、代わりに setPrototypeOf()
を使用して Human
を継承しています。
警告: extends
の代わりに setPrototypeOf()
を使うことは、パフォーマンスや可読性の点からお勧めできません。
class Human {}
class SuperHero {}
// 継承プロパティを設定
Object.setPrototypeOf(SuperHero.prototype, Human.prototype);
// 静的プロパティをフック
Object.setPrototypeOf(SuperHero, Human);
const superMan = new SuperHero();
extends
を使わないサブクラス化は、 ES-6 subclassing で触れられています。
仕様書
Specification |
---|
ECMAScript® 2025 Language Specification # sec-object.setprototypeof |