タイトル: Object(JavaScriptビルトイン)
SEOタイトル: JavaScript Object 完全ガイド(リテラル / keys/values/entries / freeze / Optional Chaining)
| この記事の要点 |
|
オブジェクトの生成
// ✅ リテラル(推奨)
const user = { name: 'taro', age: 20 };
// new Object() / Object() (非推奨だが動く)
const empty = new Object(); // = {}
// プロトタイプ無し(純粋な辞書として使う)
const dict = Object.create(null);
dict.foo = 1;
// → 'toString' 等を継承しないのでキー衝突しない
// 既存オブジェクトをプロトタイプに
const child = Object.create({ greet() { return 'hi'; } });
child.greet(); // 'hi'
主要な静的メソッド
| メソッド | 用途 | 戻り値 |
|---|---|---|
Object.keys(o) | キーの配列 | string[] |
Object.values(o) | 値の配列 | any[] |
Object.entries(o) | [key, value] の配列 | [string, any][] |
Object.fromEntries(arr) | entries の逆。配列から Object | object |
Object.assign(t, ...s) | マージ(破壊的) | マージ後の t |
Object.freeze(o) | 変更不可化(シャロー) | o |
Object.isFrozen(o) | 凍結済か判定 | boolean |
Object.create(p) | プロトタイプ指定生成 | object |
Object.defineProperty(o,k,d) | 詳細属性付きでプロパティ追加 | o |
Object.getPrototypeOf(o) | プロトタイプ取得 | object|null |
Object.hasOwn(o, k) (ES2022) | 自身のプロパティか判定 | boolean |
Object.is(a, b) | 厳密同値(NaN/0 を正確に) | boolean |
keys / values / entries の活用
const prices = { apple: 100, banana: 80, cherry: 300 };
// 値の合計
Object.values(prices).reduce((a, b) => a + b, 0); // 480
// 200 円以上だけ抜き出して新オブジェクトに
const expensive = Object.fromEntries(
Object.entries(prices).filter(([, v]) => v >= 200)
);
// { cherry: 300 }
// 値を 1.1 倍に
const taxed = Object.fromEntries(
Object.entries(prices).map(([k, v]) => [k, Math.round(v * 1.1)])
);
// { apple: 110, banana: 88, cherry: 330 }
マージ: Object.assign と Spread
const base = { theme: 'light', size: 'M' };
const user = { size: 'L', color: 'red' };
// Object.assign(破壊的: 第一引数が書き換わる)
const merged1 = Object.assign({}, base, user);
// { theme: 'light', size: 'L', color: 'red' } ← 後勝ち
// Spread(推奨)
const merged2 = { ...base, ...user };
// { theme: 'light', size: 'L', color: 'red' }
// React の state 更新でよく使う形
setUser(prev => ({ ...prev, name: '新しい名前' }));
// ⚠️ ネストはシャローコピー
const a = { profile: { name: 'taro' } };
const b = { ...a };
b.profile.name = 'jiro';
console.log(a.profile.name); // 'jiro' ← 一緒に変わる!
// ディープコピーが必要なら structuredClone
const c = structuredClone(a);
c.profile.name = 'saburo';
console.log(a.profile.name); // 'taro' のまま
Optional Chaining と Nullish Coalescing
const user = { profile: { address: { city: 'Tokyo' } } };
// ❌ 旧来: 段階的に存在チェック
const city1 = user && user.profile && user.profile.address && user.profile.address.city;
// ✅ Optional Chaining (ES2020)
const city2 = user?.profile?.address?.city;
// undefined にならず "Tokyo"。途中で null/undefined なら undefined
// 関数呼び出し・配列アクセスにも
user?.greet?.();
arr?.[0];
// Nullish Coalescing (ES2020): null / undefined のときだけデフォルト
const c = user?.profile?.address?.city ?? '不明';
// || との違い: '' や 0 を残すか落とすか
const name = '' || 'デフォルト'; // 'デフォルト'('' は falsy)
const name2 = '' ?? 'デフォルト'; // ''(null/undefined ではないので残る)
凍結とシール
const config = { apiUrl: '/api', timeout: 5000 };
Object.freeze(config);
config.timeout = 100; // ❌ 黙って失敗。strict mode では TypeError
delete config.apiUrl; // ❌ 同じく
config.newKey = 'x'; // ❌ 同じく
Object.isFrozen(config); // true
// ⚠️ シャロー: ネストは凍結されない
const settings = { theme: { color: 'red' } };
Object.freeze(settings);
settings.theme.color = 'blue'; // 通る!
// → ディープフリーズは自前で再帰実装が必要
// Object.seal: 追加/削除は不可、既存値の変更は可
const o = { a: 1 };
Object.seal(o);
o.a = 2; // ✅
o.b = 3; // ❌
delete o.a; // ❌
プロパティ判定
const o = { foo: 1 };
// in 演算子(プロトタイプチェーンも含む)
'foo' in o; // true
'toString' in o; // true ← Object.prototype 由来
// hasOwnProperty(自分自身だけ)
o.hasOwnProperty('foo'); // true
o.hasOwnProperty('toString'); // false
// ⚠️ Object.create(null) のオブジェクトでは hasOwnProperty 自体が無い
const dict = Object.create(null);
dict.hasOwnProperty('foo'); // TypeError
// ✅ Object.hasOwn (ES2022) は安全
Object.hasOwn(dict, 'foo'); // false(エラーにならない)
Object.hasOwn(o, 'toString'); // false
JSON 化と復元
const data = { name: 'taro', age: 20, joined: new Date() };
// 文字列化
const json = JSON.stringify(data);
const pretty = JSON.stringify(data, null, 2); // インデント付き
// パース
const obj = JSON.parse(json);
// ⚠️ Date は文字列に戻るのみ。reviver で復元する
const obj2 = JSON.parse(json, (k, v) => k === 'joined' ? new Date(v) : v);
// ⚠️ 循環参照は stringify でエラー
const cyc = { name: 'a' }; cyc.self = cyc;
JSON.stringify(cyc); // TypeError: Converting circular structure
Proxy と Reflect
// Proxy: プロパティアクセスをフック
const target = { count: 0 };
const proxy = new Proxy(target, {
get(t, k) {
console.log(`get ${k}`);
return Reflect.get(t, k);
},
set(t, k, v) {
console.log(`set ${k} = ${v}`);
return Reflect.set(t, k, v);
},
});
proxy.count = 1; // set count = 1
proxy.count; // get count → 1
// Vue 3 / MobX のリアクティブの裏側はこの仕組み
FAQ
Q: Object.keys の順序は保証される?
A: ES2015+ で「整数キー → 文字列キー(挿入順)→ Symbol キー」と仕様化されています。
Q: Map と Object の使い分けは?
A: キーが文字列・キー集合が固定なら Object、キーが任意(オブジェクト含む)・サイズが頻繁に変わるなら Map。サイズ取得・反復は Map のほうが速い。
Q: {...obj} でクラスインスタンスを複製するのは?
A: プロトタイプが失われ「ただのオブジェクト」になります。クラスを保ちたいなら Object.assign(new Foo(), obj) か structuredClone。