JavaScriptのObject.create
メソッドは、新しいオブジェクトを作成するときに、そのプロトタイプオブジェクトを指定するために使用されます。
これにより、既存のオブジェクトを新しいオブジェクトのプロトタイプとして設定することで、プロトタイプ継承の仕組みを柔軟に制御できます。
new
演算子を使ったオブジェクト作成とは異なり、コンストラクタ関数を介さずに直接プロトタイプチェーンを構築できるため、特定の継承パターンや純粋な辞書オブジェクトを作成する際に非常に強力です。
この記事では、Object.create
メソッドの基本的な使い方、主要な使用例、そして使用する際の重要な注意点について解説します。
Object.createメソッドの基本的な構文
Object.createメソッドの基本的な構文は以下の通りです。
Object.create(proto, [propertiesObject])
proto
: 必須。新しく作成されるオブジェクトのプロトタイプとなるオブジェクトです。- オブジェクトであるか、
null
でなければなりません。 null
を指定した場合、プロトタイプチェーンを持たない(Object.prototype
を継承しない)純粋なオブジェクトが作成されます。
- オブジェクトであるか、
propertiesObject
: オプション。新しく作成されるオブジェクトに直接追加されるプロパティを定義するオブジェクトです。- この引数は、
Object.defineProperties()
の第2引数と同じ形式を取ります。つまり、プロパティディスクリプタ(value
,writable
,enumerable
,configurable
)を含むオブジェクトです。
- この引数は、
proto:プロトタイプを指定する
- 新しく作成されるオブジェクトが継承するプロトタイプを指定します。
null
を指定すると、プロトタイプチェーンの終端となり、Object.prototype
のメソッド(例:toString()
,hasOwnProperty()
など)を継承しないオブジェクトが作成されます。このようなオブジェクトは、純粋なキー/値のペアを格納する辞書として使用されることがあります。
propertiesObject:プロパティを定義する
- 新しいオブジェクトに直接追加したいプロパティを定義します。
- 各プロパティは、キーと、そのプロパティの属性を定義するディスクリプタオブジェクトのペアとして指定されます。
value
: プロパティの値。writable
:true
の場合、プロパティの値を変更できる。デフォルトはfalse
。enumerable
:true
の場合、for...in
ループやObject.keys()
で列挙できる。デフォルトはfalse
。configurable
:true
の場合、プロパティのディスクリプタを変更したり、プロパティを削除したりできる。デフォルトはfalse
。get
: ゲッター関数。プロパティが読み取られたときに呼び出される。set
: セッター関数。プロパティが設定されたときに呼び出される。
Object.createの戻り値
指定されたプロトタイプとプロパティを持つ新しいオブジェクトが返されます。
Object.createメソッドを使ってみる
実際にObject.create
を使って、動作を確認していきます。
例1:基本的なプロトタイプ継承
既存のオブジェクトをプロトタイプとして新しいオブジェクトを作成します。
const animal = {
eats: true,
walk() {
console.log("動物が歩きます。");
}
};
// animal をプロトタイプとして新しいオブジェクト rabbit を作成
const rabbit = Object.create(animal);
rabbit.jumps = true; // rabbit 自身のプロパティを追加
console.log(rabbit.eats); // 出力: true (animal から継承)
rabbit.walk(); // 出力: 動物が歩きます。(animal から継承)
console.log(rabbit.jumps); // 出力: true (rabbit 自身のプロパティ)
console.log(Object.getPrototypeOf(rabbit) === animal); // 出力: true
rabbit
はanimal
のプロパティとメソッドを継承していますが、jumps
はrabbit
自身のプロパティです。
このように既存のオブジェクトを継承しつつ、新しいプロパティやメソッドを追加できます。
例2:プロパティディスクリプタを使用してプロパティを追加する
新しいオブジェクトを作成する際に、同時にプロパティとその属性を定義します。
const baseUser = {
role: 'guest',
greet() {
console.log(`Hello, I am a ${this.role}.`);
}
};
// baseUser をプロトタイプとし、name と age プロパティを定義
const adminUser = Object.create(baseUser, {
name: {
value: 'Admin User',
writable: true, // 変更可能
enumerable: true, // 列挙可能
configurable: true // 削除・再設定可能
},
age: {
value: 40,
writable: false, // 変更不可
enumerable: true,
configurable: false
},
role: { // プロトタイプのroleを上書きする自身のプロパティ
value: 'admin',
writable: true,
enumerable: true,
configurable: true
}
});
adminUser.greet(); // 出力: Hello, I am a admin. (adminUser自身のroleが優先)
console.log(adminUser.name); // 出力: Admin User
adminUser.name = 'Super Admin'; // writable: true なので変更可能
console.log(adminUser.name); // 出力: Super Admin
// adminUser.age = 41; // writable: false なのでエラー (厳格モードの場合)
// console.log(adminUser.age);
console.log(Object.getPrototypeOf(adminUser) === baseUser); // 出力: true
adminUser
はbaseUser
を継承しつつ、name
とage
、そしてrole
という自身のプロパティを持っています。
role
を加えることで、baseUser
のrole
をシャドウ(上書き)しています。
adminUser.age
はwritable: false
なので変更できません。
出力結果を見るとrole
が上書きされていること、name
を追加して上書きできることが確認できました。
Hello, I am a admin.
Admin User
Super Admin
true
例3:nullをプロトタイプとして使用する(純粋な辞書オブジェクト)
プロトタイプチェーンを持たない、完全に空のオブジェクト(Object.prototype
のプロパティを継承しない)を作成します。
// プロトタイプチェーンを持たない純粋な辞書オブジェクトを作成
const pureDictionary = Object.create(null);
pureDictionary.key1 = 'value1';
pureDictionary.key2 = 123;
console.log(pureDictionary.key1); // 出力: value1
// Object.prototype のメソッドは継承しないため、存在しない
// console.log(pureDictionary.toString()); // エラー: pureDictionary.toString is not a function
// hasOwnProperty は存在しないが、Object.prototypeから直接呼び出すことは可能
console.log(Object.prototype.hasOwnProperty.call(pureDictionary, 'key1')); // 出力: true
console.log(Object.getPrototypeOf(pureDictionary)); // 出力: null
pureDictionary
はObject.prototype
を継承しないため、toString()
やhasOwnProperty()
などの標準メソッドを持ちません。
これは、JSONデータのように純粋なキーと値のペアを扱う場合に、予期せぬプロトタイププロパティによる干渉を防ぐのに役立ちます。
例4:既存のオブジェクトをクローンする(シャローコピー)
Object.assign({}, original)
と同様に、Object.create
もオブジェクトのシャローコピーを作成するために使用できます。
const originalObj = {
a: 1,
b: { c: 2 },
d: [3, 4]
};
// originalObj をプロトタイプとして新しいオブジェクトを作成し、
// originalObj の自身のプロパティを新しいオブジェクトにコピー
const clonedObj = Object.create(Object.getPrototypeOf(originalObj), Object.getOwnPropertyDescriptors(originalObj));
console.log(clonedObj);
// 出力: { a: 1, b: { c: 2 }, d: [ 3, 4 ] }
// シャローコピーの挙動を確認
clonedObj.a = 10;
clonedObj.b.c = 20; // ネストされたオブジェクトは参照がコピーされるため、元のオブジェクトも変更される
clonedObj.d.push(5); // ネストされた配列も参照がコピーされるため、元のオブジェクトも変更される
console.log(originalObj.a); // 出力: 1 (変更なし)
console.log(originalObj.b.c); // 出力: 20 (変更された!)
console.log(originalObj.d); // 出力: [ 3, 4, 5 ] (変更された!)
この例では、Object.getPrototypeOf(originalObj)
で元のプロトタイプを取得し、Object.getOwnPropertyDescriptors(originalObj)
で元のオブジェクトの全プロパティディスクリプタを取得して新しいオブジェクトにコピーしています。
これにより、originalObj
のプロパティがclonedObj
自身のプロパティとしてコピーされます。
しかし、Object.assign
と同様に、これはシャローコピーであるため、ネストされたオブジェクトや配列の変更は元のオブジェクトにも影響します。
Object.createを使う際の重要な注意点
Object.createを使うときの注意点です。
シャローコピーであること
Object.create
は、propertiesObject
を使ってプロパティを直接定義する場合も、プロトタイプとしてオブジェクトを継承する場合も、シャローコピーの原則に従います。
ネストされたオブジェクトや配列は参照渡しでコピーされるため、深いコピー(ディープコピー)が必要な場合は、別の方法(JSON.parse(JSON.stringify(obj))
やライブラリなど)を検討してください。
コンストラクタは呼び出されない
new
演算子とは異なり、Object.create
はプロトタイプとして指定されたオブジェクトのコンストラクタ関数を呼び出しません。
これは、コンストラクタ内で初期化処理が行われるオブジェクトを扱う場合に注意が必要です。
プロトタイプチェーンの理解が必須
Object.create
を効果的に使うには、JavaScriptのプロトタイプ継承の仕組みを深く理解している必要があります。
nullをプロトタイプにする場合
Object.create(null)
で作成されたオブジェクトは、Object.prototype
からのメソッドを一切継承しません。
これは、hasOwnProperty
や toString
などの便利なメソッドも利用できないことを意味します。
純粋な辞書として使う場合以外は、通常は既存のオブジェクトをプロトタイプとして指定するか、リテラル構文 ({}
) でオブジェクトを作成する方が一般的です。
まとめ
JavaScriptのObject.create
メソッドは、新しいオブジェクトを作成し、そのプロトタイプを柔軟に指定するための強力なツールです。
コンストラクタ関数を介さずにプロトタイプ継承を直接制御できるため、特定の継承パターンを実装したり、Object.prototype
を継承しない純粋な辞書オブジェクトを作成したりする際に非常に便利です。
しかし、シャローコピーであること、コンストラクタが呼び出されないこと、そしてプロトタイプ継承の仕組みを深く理解している必要があるという重要な注意点を踏まえて
Object.create
を効果的に活用し、JavaScriptアプリケーションにおけるプログラミングをより柔軟に行いましょう。
コメント