7.

JavaScript Objectの使い方|keys/values/entries・freeze・Optional Chaining

編集
この記事の要点
  • JavaScript の Object はキーと値のペアの集合。リテラル { key: value } で生成
  • Object.keys / values / entries で配列化し map / filter / reduce に流す
  • Object.assign / spread {...a, ...b} でマージ。後勝ち。シャローコピーである点に注意
  • Object.freeze で書き換え禁止(シャロー)。Object.create(null) でプロトタイプ無しオブジェクト
  • Optional Chaining ?.Nullish Coalescing ?? で安全アクセス。Object.hasOwn(obj, key) (ES2022) は hasOwnProperty より安全

オブジェクトの生成

// ✅ リテラル(推奨)
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 の逆。配列から Objectobject
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

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. JSON
  2. Array
  3. Boolean(JavaScriptビルトイン)
  4. Date オブジェクト
  5. Math (JS ビルトイン)
  6. Number
  7. Object(JavaScriptビルトイン)
  8. RegExp

最近更新/作成されたページ