はじめに

JavaScriptでオブジェクトを扱う際、Object クラスには多数の便利な静的メソッドが用意されています。これらのメソッドを活用することで、オブジェクトのキーや値を配列として取得したり、オブジェクト同士をマージしたり、オブジェクトの変更を防止したりといった操作を簡潔に行えます。

本記事では、以下のObject静的メソッドについて、初心者にもわかりやすく解説します。

  • オブジェクトの列挙: Object.keys()Object.values()Object.entries()
  • 配列からオブジェクトへの変換: Object.fromEntries()
  • オブジェクトのコピーとマージ: Object.assign()
  • オブジェクトの変更防止: Object.freeze()Object.seal()
  • プロパティの存在確認: Object.hasOwn()
  • その他の便利なメソッド: Object.is()Object.groupBy()

Object静的メソッドとは

Object静的メソッドとは、Object コンストラクター関数に直接定義されているメソッドのことです。インスタンスメソッド(例: obj.toString())とは異なり、オブジェクトのインスタンスではなく Object 自体に対して呼び出します。

1
2
3
4
5
6
7
// インスタンスメソッド(オブジェクトに対して呼び出す)
const user = { name: "田中" };
console.log(user.toString()); // "[object Object]"

// 静的メソッド(Objectに対して呼び出す)
const keys = Object.keys(user);
console.log(keys); // ["name"]

静的メソッドを使うメリットは、オブジェクト自体を変更せずにデータを取得・変換できる点と、null プロトタイプのオブジェクトでも安全に操作できる点です。

Object.keys() - キーを配列として取得

Object.keys() は、オブジェクトのすべての列挙可能なプロパティ名(キー)を配列として返します。

基本的な使い方

1
2
3
4
5
6
7
8
const user = {
  name: "田中太郎",
  age: 25,
  city: "東京"
};

const keys = Object.keys(user);
console.log(keys); // ["name", "age", "city"]

配列の長さでプロパティ数を確認

1
2
3
4
5
6
7
8
const product = {
  id: 1,
  name: "ノートPC",
  price: 89800,
  stock: 15
};

console.log(Object.keys(product).length); // 4

ループ処理との組み合わせ

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
const scores = {
  math: 85,
  english: 72,
  science: 90
};

// forEachでキーを使ってループ
Object.keys(scores).forEach(subject => {
  console.log(`${subject}: ${scores[subject]}点`);
});
// math: 85点
// english: 72点
// science: 90点

注意点: 継承されたプロパティは含まれない

Object.keys() はオブジェクト自身が持つプロパティのみを返します。プロトタイプチェーンから継承されたプロパティは含まれません。

1
2
3
4
5
const animal = { type: "動物" };
const dog = Object.create(animal);
dog.name = "ポチ";

console.log(Object.keys(dog)); // ["name"]("type"は含まれない)

Object.values() - 値を配列として取得

Object.values() は、オブジェクトのすべての列挙可能なプロパティの値を配列として返します。

基本的な使い方

1
2
3
4
5
6
7
8
const user = {
  name: "田中太郎",
  age: 25,
  city: "東京"
};

const values = Object.values(user);
console.log(values); // ["田中太郎", 25, "東京"]

数値の集計

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
const scores = {
  math: 85,
  english: 72,
  science: 90
};

// 合計点を計算
const total = Object.values(scores).reduce((sum, score) => sum + score, 0);
console.log(total); // 247

// 平均点を計算
const average = total / Object.values(scores).length;
console.log(average); // 82.33...

特定の値が存在するか確認

1
2
3
4
5
6
7
8
9
const settings = {
  theme: "dark",
  language: "ja",
  notifications: true
};

// "dark"という値が存在するか
const hasDark = Object.values(settings).includes("dark");
console.log(hasDark); // true

Object.entries() - キーと値のペアを取得

Object.entries() は、オブジェクトのキーと値のペアを [key, value] 形式の二次元配列として返します。

基本的な使い方

1
2
3
4
5
6
7
8
9
const user = {
  name: "田中太郎",
  age: 25,
  city: "東京"
};

const entries = Object.entries(user);
console.log(entries);
// [["name", "田中太郎"], ["age", 25], ["city", "東京"]]

分割代入を使ったループ

for...of と分割代入を組み合わせることで、キーと値を同時に取得しながらループできます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
const product = {
  name: "スマートフォン",
  price: 89800,
  color: "ブラック"
};

for (const [key, value] of Object.entries(product)) {
  console.log(`${key}: ${value}`);
}
// name: スマートフォン
// price: 89800
// color: ブラック

オブジェクトの変換処理

キーと値を同時に変換したい場合に便利です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
const prices = {
  apple: 100,
  banana: 80,
  orange: 120
};

// すべての価格を10%値上げ
const increasedPrices = Object.fromEntries(
  Object.entries(prices).map(([fruit, price]) => [fruit, Math.round(price * 1.1)])
);

console.log(increasedPrices);
// { apple: 110, banana: 88, orange: 132 }

Object.keys、values、entriesの比較

3つのメソッドの違いを以下の表にまとめます。

メソッド 戻り値
Object.keys() キーの配列 ["name", "age"]
Object.values() 値の配列 ["田中", 25]
Object.entries() [key, value]の配列 [["name", "田中"], ["age", 25]]
flowchart LR
    A[オブジェクト] --> B["Object.keys()"]
    A --> C["Object.values()"]
    A --> D["Object.entries()"]
    B --> E["['name', 'age']"]
    C --> F["['田中', 25]"]
    D --> G["[['name', '田中'], ['age', 25]]"]

Object.fromEntries() - 配列からオブジェクトを作成

Object.fromEntries() は、[key, value] 形式の配列からオブジェクトを作成します。Object.entries() の逆の操作です。

基本的な使い方

1
2
3
4
5
6
7
8
9
const entries = [
  ["name", "田中太郎"],
  ["age", 25],
  ["city", "東京"]
];

const user = Object.fromEntries(entries);
console.log(user);
// { name: "田中太郎", age: 25, city: "東京" }

Mapからオブジェクトへの変換

Map オブジェクトをプレーンなオブジェクトに変換する場合にも使えます。

1
2
3
4
5
6
7
8
9
const map = new Map([
  ["id", 1],
  ["name", "商品A"],
  ["price", 1980]
]);

const obj = Object.fromEntries(map);
console.log(obj);
// { id: 1, name: "商品A", price: 1980 }

URLSearchParamsからオブジェクトへの変換

URLのクエリパラメータをオブジェクトに変換する際にも便利です。

1
2
3
4
const params = new URLSearchParams("page=1&limit=20&sort=date");
const queryObj = Object.fromEntries(params);
console.log(queryObj);
// { page: "1", limit: "20", sort: "date" }

オブジェクトのフィルタリング

Object.entries() と組み合わせて、特定の条件に合うプロパティだけを抽出できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
const scores = {
  math: 85,
  english: 45,
  science: 90,
  history: 55
};

// 60点以上の科目だけを抽出
const passed = Object.fromEntries(
  Object.entries(scores).filter(([subject, score]) => score >= 60)
);

console.log(passed);
// { math: 85, science: 90 }

Object.assign() - オブジェクトのコピーとマージ

Object.assign() は、1つ以上のソースオブジェクトのプロパティをターゲットオブジェクトにコピーします。

基本構文

1
Object.assign(target, source1, source2, ...);
  • target: コピー先のオブジェクト(変更される)
  • source: コピー元のオブジェクト(複数指定可能)

オブジェクトのコピー(シャローコピー)

1
2
3
4
5
const original = { a: 1, b: 2, c: 3 };
const copy = Object.assign({}, original);

console.log(copy); // { a: 1, b: 2, c: 3 }
console.log(copy === original); // false(異なるオブジェクト)

複数のオブジェクトをマージ

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
const defaults = {
  theme: "light",
  language: "en",
  showNotifications: true
};

const userSettings = {
  theme: "dark",
  fontSize: 14
};

const merged = Object.assign({}, defaults, userSettings);
console.log(merged);
// {
//   theme: "dark",
//   language: "en",
//   showNotifications: true,
//   fontSize: 14
// }

後から指定したオブジェクトのプロパティで上書きされる点に注意してください。

スプレッド構文との比較

ES6以降では、スプレッド構文を使うとより簡潔に書けます。

1
2
3
4
5
// Object.assign()
const merged1 = Object.assign({}, defaults, userSettings);

// スプレッド構文(推奨)
const merged2 = { ...defaults, ...userSettings };

どちらも同じ結果になりますが、スプレッド構文のほうが可読性が高いため、現代のJavaScriptではスプレッド構文が好まれます。

注意点: シャローコピーである

Object.assign() はシャローコピー(浅いコピー)を行います。ネストしたオブジェクトは参照がコピーされるため、元のオブジェクトと共有されます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
const original = {
  name: "田中",
  address: {
    city: "東京",
    zip: "100-0001"
  }
};

const copy = Object.assign({}, original);

// ネストしたオブジェクトを変更
copy.address.city = "大阪";

// 元のオブジェクトも変更される
console.log(original.address.city); // "大阪"

ディープコピーが必要な場合は、structuredClone()JSON.parse(JSON.stringify()) を使用します。

1
2
3
4
5
// structuredClone()によるディープコピー(推奨)
const deepCopy = structuredClone(original);

// JSON経由のディープコピー(関数やundefinedは失われる)
const jsonCopy = JSON.parse(JSON.stringify(original));

Object.freeze() - オブジェクトを凍結する

Object.freeze() は、オブジェクトを凍結して変更を完全に防止します。凍結されたオブジェクトに対する変更はすべて無視されます(strictモードではエラー)。

基本的な使い方

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
const config = {
  apiUrl: "https://api.example.com",
  timeout: 5000
};

Object.freeze(config);

// 変更しようとしても無視される
config.timeout = 10000;
config.newProperty = "test";
delete config.apiUrl;

console.log(config);
// { apiUrl: "https://api.example.com", timeout: 5000 }

凍結状態の確認

1
2
3
4
5
const obj = { a: 1 };
console.log(Object.isFrozen(obj)); // false

Object.freeze(obj);
console.log(Object.isFrozen(obj)); // true

定数オブジェクトとしての活用

設定値やマスターデータなど、変更されては困るオブジェクトに使用します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
const HTTP_STATUS = Object.freeze({
  OK: 200,
  CREATED: 201,
  BAD_REQUEST: 400,
  UNAUTHORIZED: 401,
  NOT_FOUND: 404,
  INTERNAL_SERVER_ERROR: 500
});

// 誤って変更しようとしても安全
HTTP_STATUS.OK = 999; // 無視される
console.log(HTTP_STATUS.OK); // 200

注意点: シャローフリーズである

Object.freeze() もシャローな操作です。ネストしたオブジェクトは凍結されません。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
const config = Object.freeze({
  server: {
    host: "localhost",
    port: 3000
  }
});

// ネストしたオブジェクトは変更可能
config.server.port = 8080;
console.log(config.server.port); // 8080

ディープフリーズを行うには、再帰的に Object.freeze() を適用する関数を作成します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
function deepFreeze(obj) {
  Object.keys(obj).forEach(key => {
    if (typeof obj[key] === "object" && obj[key] !== null) {
      deepFreeze(obj[key]);
    }
  });
  return Object.freeze(obj);
}

const config = deepFreeze({
  server: {
    host: "localhost",
    port: 3000
  }
});

config.server.port = 8080; // 無視される
console.log(config.server.port); // 3000

Object.seal() - オブジェクトを封印する

Object.seal() は、オブジェクトを封印してプロパティの追加・削除を防止します。ただし、既存プロパティの値の変更は許可されます。

基本的な使い方

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
const user = {
  name: "田中太郎",
  age: 25
};

Object.seal(user);

// 既存プロパティの変更は可能
user.age = 26;
console.log(user.age); // 26

// 新しいプロパティの追加は無視される
user.email = "tanaka@example.com";
console.log(user.email); // undefined

// プロパティの削除も無視される
delete user.name;
console.log(user.name); // "田中太郎"

freezeとsealの違い

操作 freeze seal
プロパティの追加 不可 不可
プロパティの削除 不可 不可
プロパティ値の変更 不可 可能
1
2
3
4
5
6
7
8
const frozen = Object.freeze({ value: 1 });
const sealed = Object.seal({ value: 1 });

frozen.value = 2;
sealed.value = 2;

console.log(frozen.value); // 1(変更不可)
console.log(sealed.value); // 2(変更可能)

Object.hasOwn() - プロパティの存在確認

Object.hasOwn() は、オブジェクトが指定したプロパティを自身のプロパティとして持っているかを確認します。ES2022で追加された推奨メソッドです。

基本的な使い方

1
2
3
4
5
6
7
const user = {
  name: "田中太郎",
  age: 25
};

console.log(Object.hasOwn(user, "name"));  // true
console.log(Object.hasOwn(user, "email")); // false

hasOwnProperty()との違い

従来の hasOwnProperty() はインスタンスメソッドのため、オブジェクト自身に同名のプロパティがあると問題が起きます。Object.hasOwn() は静的メソッドなのでこの問題を回避できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// hasOwnPropertyが上書きされているケース
const data = {
  hasOwnProperty: () => false,
  id: 1
};

// インスタンスメソッドは上書きされた関数を呼ぶ
console.log(data.hasOwnProperty("id")); // false(誤った結果)

// 静的メソッドは安全
console.log(Object.hasOwn(data, "id")); // true(正しい結果)

nullプロトタイプオブジェクトでも安全

1
2
3
4
5
6
7
8
const nullProtoObj = Object.create(null);
nullProtoObj.id = 1;

// hasOwnPropertyは存在しない
// nullProtoObj.hasOwnProperty("id"); // TypeError

// Object.hasOwn()は安全に使える
console.log(Object.hasOwn(nullProtoObj, "id")); // true

Object.is() - 厳密な値の比較

Object.is() は、2つの値が同じ値かどうかを判定します。=== 演算子に似ていますが、いくつかのエッジケースで異なる結果を返します。

基本的な使い方

1
2
3
console.log(Object.is(25, 25));        // true
console.log(Object.is("hello", "hello")); // true
console.log(Object.is({}, {}));        // false(異なるオブジェクト)

===との違い

1
2
3
4
5
6
7
// NaNの比較
console.log(NaN === NaN);         // false
console.log(Object.is(NaN, NaN)); // true

// +0と-0の比較
console.log(0 === -0);            // true
console.log(Object.is(0, -0));    // false

Object.is() は「同一値等価性(Same-value equality)」アルゴリズムを使用するため、NaN は自分自身と等しく、+0-0 は区別されます。

Object.groupBy() - 配列のグルーピング

Object.groupBy() は、配列の要素を指定した条件でグループ化します。ES2024で標準化された比較的新しいメソッドです。

基本的な使い方

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
const products = [
  { name: "りんご", category: "果物", price: 150 },
  { name: "にんじん", category: "野菜", price: 100 },
  { name: "バナナ", category: "果物", price: 120 },
  { name: "キャベツ", category: "野菜", price: 200 }
];

const grouped = Object.groupBy(products, product => product.category);

console.log(grouped);
// {
//   "果物": [
//     { name: "りんご", category: "果物", price: 150 },
//     { name: "バナナ", category: "果物", price: 120 }
//   ],
//   "野菜": [
//     { name: "にんじん", category: "野菜", price: 100 },
//     { name: "キャベツ", category: "野菜", price: 200 }
//   ]
// }

条件によるグルーピング

1
2
3
4
5
6
7
8
9
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

const grouped = Object.groupBy(numbers, n => n % 2 === 0 ? "偶数" : "奇数");

console.log(grouped);
// {
//   "奇数": [1, 3, 5, 7, 9],
//   "偶数": [2, 4, 6, 8, 10]
// }

注意: ブラウザ対応

Object.groupBy() はES2024で標準化されましたが、古いブラウザでは対応していない場合があります。対応状況を確認するか、ポリフィルの使用を検討してください。

実践的な活用パターン

パターン1: オブジェクトの特定プロパティだけを抽出

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
const user = {
  id: 1,
  name: "田中太郎",
  email: "tanaka@example.com",
  password: "secret123",
  createdAt: "2024-01-01"
};

// 公開可能なプロパティだけを抽出
const publicFields = ["id", "name", "email"];
const publicUser = Object.fromEntries(
  Object.entries(user).filter(([key]) => publicFields.includes(key))
);

console.log(publicUser);
// { id: 1, name: "田中太郎", email: "tanaka@example.com" }

パターン2: オブジェクトのキーをリネーム

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
const apiResponse = {
  user_name: "田中太郎",
  user_age: 25,
  user_email: "tanaka@example.com"
};

// キーをキャメルケースに変換
const camelCased = Object.fromEntries(
  Object.entries(apiResponse).map(([key, value]) => {
    const newKey = key.replace(/_([a-z])/g, (_, char) => char.toUpperCase());
    return [newKey, value];
  })
);

console.log(camelCased);
// { userName: "田中太郎", userAge: 25, userEmail: "tanaka@example.com" }

パターン3: オブジェクトの差分を検出

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
function getChangedFields(original, updated) {
  return Object.fromEntries(
    Object.entries(updated).filter(([key, value]) => original[key] !== value)
  );
}

const before = { name: "田中", age: 25, city: "東京" };
const after = { name: "田中", age: 26, city: "大阪" };

const changes = getChangedFields(before, after);
console.log(changes);
// { age: 26, city: "大阪" }

まとめ

この記事では、JavaScriptのObject静的メソッドについて解説しました。

メソッド 用途
Object.keys() キーの配列を取得
Object.values() 値の配列を取得
Object.entries() [key, value] ペアの配列を取得
Object.fromEntries() 配列からオブジェクトを作成
Object.assign() オブジェクトのコピー・マージ
Object.freeze() オブジェクトを凍結(変更不可)
Object.seal() オブジェクトを封印(追加・削除不可)
Object.hasOwn() プロパティの存在確認
Object.is() 厳密な値の比較
Object.groupBy() 配列のグルーピング

これらのメソッドを使いこなすことで、オブジェクトの操作がより簡潔かつ安全に行えるようになります。特に Object.keys()Object.values()Object.entries()Object.fromEntries() の組み合わせは、オブジェクトの変換処理で頻繁に使用するパターンですので、ぜひ実践で活用してみてください。

参考リンク