オブジェクトリテラル {} とプロパティ
一番シンプルなオブジェクト
const user = {
name: "Taro",
age: 20,
};{}が「オブジェクトリテラル」name/ageが「プロパティ名(キー)」"Taro"/20が「プロパティ値」
プロパティは「キーと値のペア」の集まりです。
値の型は何でもOK
const user = {
name: "Taro", // 文字列
age: 20, // 数値
isAdmin: false, // 真偽値
favorites: ["JS"], // 配列
address: { city: "東京" }, // オブジェクト
greet: function () { // 関数(メソッド)
console.log("こんにちは");
},
};メソッド記法(短縮形)
関数プロパティは、短く書けます:
const user = {
name: "Taro",
greet() {
console.log(`こんにちは、${this.name}です`);
},
};
user.greet(); // こんにちは、Taroですgreet: function () { ... }の省略形
プロパティ名と識別子
プロパティ名は基本的には 文字列 として扱われます。
const obj = {
foo: 1,
"bar-baz": 2,
};fooも"foo"も「キー文字列'foo'」として解釈される- ただし、識別子のルールを守らない名前(例:
"bar-baz")は、ドット記法でアクセスできません
プロパティ名には Symbol も使えますが、ここでは「キー=(ほぼ)文字列」と思っておいてOKです。
プロパティアクセス(ドット / ブラケット)
ドット記法
const user = { name: "Taro", age: 20 };
console.log(user.name); // "Taro"
console.log(user.age); // 20- 一番よく使う形
- 右側は 有効な識別子 である必要がある
- 先頭に数字NG、スペースや記号NG
ブラケット記法
const user = { name: "Taro", age: 20 };
console.log(user["name"]); // "Taro"
console.log(user["age"]); // 20"プロパティ名文字列"を[]の中に書く- ドット記法で書けるものはどちらでもOK
ブラケット記法の真価は、「動的なキー」を使えることです。
const prop = "name";
const user = { name: "Taro" };
console.log(user[prop]); // "Taro"(user["name"])APIレスポンスなどで「どのキーが来るかわからない」ようなときに必須です。
ドットで書けないプロパティ名
const obj = {
"foo-bar": 1,
"123": 2,
};
console.log(obj["foo-bar"]); // 1
console.log(obj["123"]); // 2
// console.log(obj.foo-bar); // NG(構文としてエラー or 計算扱い)
// console.log(obj.123); // NG(構文エラー)- スペース・ハイフン・先頭数字などがあるプロパティ名 → ブラケット必須
オブジェクトは「参照型」として振る舞う
ここが重要ポイントです。
プリミティブとの違い
プリミティブ(数値・文字列・真偽値など)は 値そのもの がコピーされます。
let a = 1;
let b = a; // 値 1 がコピーされる
b = 2;
console.log(a); // 1
console.log(b); // 2オブジェクトは 参照 がコピーされます。
const obj1 = { value: 1 };
const obj2 = obj1; // 「同じオブジェクト」への参照がコピーされる
obj2.value = 2;
console.log(obj1.value); // 2 ← 変わる
console.log(obj2.value); // 2obj1とobj2は「別の箱」だが、中身として指しているオブジェクトは同じ
参照共有のイメージ
obj1 → 🧱(value: 1) ← obj2
このように、2つの変数が同じ「箱(オブジェクト)」を指しているイメージです。
オブジェクト同士の比較
const a = { x: 1 };
const b = { x: 1 };
const c = a;
console.log(a === b); // false (中身が同じでも、別オブジェクト)
console.log(a === c); // true (同じオブジェクトを指している)===は「同じオブジェクトを指しているかどうか」で比較する- 「中身が同じかどうか」を見てくれない(それは自分で比較する必要がある)
「コピーしたつもりが共有していた」バグに注意
const original = { nested: { value: 1 } };
// 「コピーしたつもり」でも、実は参照共有
const copy = original;
copy.nested.value = 999;
console.log(original.nested.value); // 999これは意図的に共有したいときは便利ですが、
「独立したコピーが欲しかったのに」という場合はバグになります。
ここではひとまず:
「オブジェクト変数を代入しても、オブジェクト本体が複製されるわけではない」
という点をしっかり押さえておいてください。
プロトタイプチェーンとプロパティ探索
「見えない親オブジェクト」= [[Prototype]]
JavaScript のオブジェクトは、内部的に
「自分の親オブジェクト(プロトタイプ)への参照」
を 1 つ持っています。
仕様ではこれを [[Prototype]] と表記します(内部スロットなので直接は触れないイメージ)。
イメージ:
obj ----> [[Prototype]] ----> さらに [[Prototype]] ----> ... ----> nullこの「鎖」が プロトタイプチェーン です。
プロパティ探索アルゴリズム
obj.prop を読むとき、エンジンは次の順で探します:
- obj 自身が
propを持っているか?- 持っていればそれを返して終了
- なければ
obj.[[Prototype]](親オブジェクト)を見に行く - それでもなければ、さらにその親…とたどっていく
- 最後の親(
[[Prototype]]がnull)まで行ってもなければundefined
コードで見ると:
const parent = { kind: "parent" };
const child = Object.create(parent); // parent をプロトタイプに持つオブジェクトを作る
child.name = "child";
console.log(child.name); // "child" (自分のプロパティ)
console.log(child.kind); // "parent" (親のプロパティ)
console.log(child.toString); // Object.prototype 由来のメソッドchildにはkindはない- 親(
parent)がkindを持っているので、それが返ってくる - さらにその親は
Object.prototypeになっており、toStringなどが定義されている
Object.getPrototypeOf と __proto__
実際にプロトタイプを覗くには:
const parent = { kind: "parent" };
const child = Object.create(parent);
console.log(Object.getPrototypeOf(child) === parent); // trueブラウザ環境などでは、慣習的に __proto__ というプロパティでも見えます(仕様上は非推奨)。
console.log(child.__proto__ === parent); // true(環境によっては見える)- 学習用にはわかりやすいですが、実務的には
Object.getPrototypeOf/Object.setPrototypeOfを使う方が推奨されます。
「自分のプロパティ」と「継承されたプロパティ」
const parent = { kind: "parent" };
const child = Object.create(parent);
child.name = "child";
console.log(child.name); // "child"
console.log(child.kind); // "parent"
console.log(child.hasOwnProperty("name")); // true
console.log(child.hasOwnProperty("kind")); // falsehasOwnPropertyは「このオブジェクト自身が持っているか」だけを見る- プロトタイプ経由で見えているプロパティは
false
※ hasOwnProperty 自体も Object.prototype から継承されているメソッドです。
上書き(シャドーイング)
子オブジェクトで同じ名前のプロパティを定義すると、親のプロパティを「隠す」 形になります。
const parent = { value: 1 };
const child = Object.create(parent);
console.log(child.value); // 1(親由来)
child.value = 2;
console.log(child.value); // 2(自分のプロパティ)
console.log(parent.value); // 1(親は変わってない)
console.log(child.hasOwnProperty("value")); // true- 最初は親の
valueが見えていた - 子で
valueを定義すると、「自分の value」が優先される - これを「プロパティのシャドーイング(shadowing)」と呼びます。
Object.create で継承チェーンを作る
Object.create を使ったシンプルな継承の例です。
const animal = {
walk() {
console.log("I am walking");
},
};
const dog = Object.create(animal); // animal をプロトタイプに
dog.bark = function () {
console.log("わん!");
};
dog.walk(); // "I am walking" (animal 由来)
dog.bark(); // "わん!" (dog 自身)dogはwalkを持っていない- プロトタイプ(
animal)をたどってwalkを見つけて実行している
コメント