JavaScriptのオブジェクト:作成と操作

2025-07-28

はじめに

JavaScriptのオブジェクトは、複数のデータや関数をまとめて管理できる「キーと値のペア」の集合体です。オブジェクトは {} を使って作成し、プロパティ(キー)には文字列やシンボルを使い、値には任意のデータや関数を設定できます。オブジェクトの操作は、ドット . やブラケット [] を使ってプロパティの取得や設定を行います。

オブジェクトの基本概念

JavaScriptのオブジェクトは、プロパティ(キーと値のペア)の集合です。

オブジェクトは現実世界の「もの」をモデル化するのに最適で、JavaScriptプログラミングの中心的な要素です。JJavaScriptのオブジェクトは、プロパティ(キーと値のペア)メソッド(関数)の集合です。

メソッドはオブジェクトの動作を定義し、オブジェクトのデータを操作したり、特定の処理を実行したりします。

プロパティの値には、文字列、数値、真偽値、配列、さらには別のオブジェクトなど、あらゆるデータ型を指定できます。以下はオブジェクトと基本データ型との違いを比較します。

基本データ型 (Primitive Types)

  • 数値 (Number): 整数や浮動小数点数
  • 文字列 (String): テキストデータ
  • 真偽値 (Boolean): true または false
  • undefined: 値が未定義
  • null: 値が存在しない
  • Symbol: 一意で不変の値
  • BigInt: 大きな整数値

基本データ型は不変 (immutable) であり、値そのものが直接変数に格納されます。

オブジェクト (Objects)

  • キーと値のペアの集合
  • 配列 (Array): 順序付けられた値の集合
  • 関数 (Function): 呼び出し可能なオブジェクト
  • 日付 (Date): 日付と時刻の情報
  • 正規表現 (RegExp): パターンマッチング
  • その他、ユーザー定義オブジェクト

オブジェクトは可変 (mutable) であり、参照によってアクセスされます。

基本データ型とオブジェクトの主な違い

特性基本データ型オブジェクト
保存されるもの実際の値メモリへの参照(ポインタ)
変更可能性不変(immutable)可変(mutable)
コピー時の動作値のコピーが作成される参照のコピーが作成される
比較時の動作値による比較参照による比較
プロパティプロパティを持てないプロパティを持てる
メモリ使用量比較的少ない比較的多い

オブジェクトの作成方法

1. オブジェクトリテラル(最も一般的)

波括弧 {} を使って直接キーと値を指定してオブジェクトを作る方法です。簡単で直感的に書けるため、最もよく使われます。

const person = {
  name: '山田太郎',
  age: 30,
  greet: function() {
    console.log(`こんにちは、${this.name}です!`);
  }
};

2. new Object() コンストラクタ

組み込みの Object コンストラクタを使ってオブジェクトを生成する方法です。ほぼオブジェクトリテラルと同じですが、記述が少し長くなります。

const car = new Object();
car.make = 'Toyota';
car.model = 'Corolla';
car.year = 2020;

3. コンストラクタ関数

関数を使ってオブジェクトの設計図を作り、new キーワードで複数のオブジェクトを生成できます。プロパティやメソッドをまとめて作りたいときに便利です。

function Person(name, age) {
  this.name = name;
  this.age = age;
  this.greet = function() {
    console.log(`こんにちは、${this.name}です!`);
  };
}

const person = new Person('山田太郎', 30);

4. Object.create() メソッド

指定したオブジェクトをプロトタイプとして持つ新しいオブジェクトを作成します。継承関係を意識したオブジェクト生成に使います。

const personPrototype = {
  greet() {
    console.log(`こんにちは、${this.name}です!`);
  }
};

const person = Object.create(personPrototype);
person.name = '山田太郎';
person.age = 30;

5. ES6クラス

クラス構文を使ってオブジェクトの設計図を定義し、newでインスタンス化します。よりわかりやすくオブジェクト指向的に書ける最新の方法です。

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  greet() {
    console.log(`こんにちは、${this.name}です!`);
  }
}

const person = new Person('山田太郎', 30);

プロパティへのアクセス

1. ドット記法

オブジェクトのプロパティにアクセスするとき、最も基本的で読みやすい方法です。プロパティ名が有効な識別子の場合に使えます。

console.log(person.name); // '山田太郎'
person.greet(); // メソッド呼び出し

2. ブラケット記法

プロパティ名を文字列として指定する方法で、プロパティ名にスペースや特殊文字が含まれる場合や変数で指定したいときに使います。

console.log(person['name']); // '山田太郎'
const prop = 'age';
console.log(person[prop]); // 30

3. 動的なプロパティ名(ES6)

オブジェクトのプロパティ名を変数や式で動的に決めたいときに、ブラケット [] を使ってプロパティ名を定義できます。オブジェクトリテラル内で使うことが多いです。

const dynamicKey = 'email';
const user = {
  name: '太郎',
  [dynamicKey]: 'taro@example.com'
};

プロパティの操作

1. プロパティの追加

オブジェクトに新しいプロパティを追加するには、ドット記法やブラケット記法で新しいキーに値を代入します。存在しなければ自動で追加されます。

person.job = 'エンジニア';
person['hobby'] = '読書';

2. プロパティの更新

既にあるプロパティに新しい値を代入することで更新できます。追加と同じ書き方です。

person.age = 31;
person['age'] = 31;

3. プロパティの削除

delete 演算子を使うと、オブジェクトから特定のプロパティを削除できます。

delete person.hobby;

4. プロパティの存在確認

in演算子やhasOwnPropertyメソッドでプロパティがオブジェクトに存在するか確認できます。

console.log('name' in person); // true
console.log(person.hasOwnProperty('name')); // true

オブジェクトのメソッド

1. オブジェクト内の関数

オブジェクトのプロパティとして関数を持つものを「メソッド」と呼びます。メソッドはオブジェクトの動作や処理を表し、オブジェクトに関連する操作をまとめて実行できます。

const calculator = {
  add: function(a, b) {
    return a + b;
  },
  // ES6メソッド短縮記法
  subtract(a, b) {
    return a - b;
  }
};

2. thisキーワード

メソッド内の this は、そのメソッドを呼び出したオブジェクトを指します。これにより、同じオブジェクトの他のプロパティにアクセスしたり操作したりできます。

const person = {
  name: '太郎',
  greet() {
    console.log(`こんにちは、${this.name}です!`);
  }
};

オブジェクトの反復処理

1. for…inループ

for…inループはオブジェクトのすべての列挙可能なプロパティ名(キー)を順番に取り出して処理するときに使います。オブジェクトのプロパティを繰り返し処理する基本的な方法ですが、継承されたプロパティも含まれるため、必要に応じてhasOwnPropertyでフィルターすることが多いです。

for (const key in person) {
  console.log(`${key}: ${person[key]}`);
}

2. Object.keys(), Object.values(), Object.entries()

これらはオブジェクトのプロパティを配列として取得する便利なメソッドです。

  • Object.keys(obj) はキーの配列を返します。
  • Object.values(obj) は値の配列を返します。
  • Object.entries(obj)[キー, 値] のペアの配列を返します。
    配列なのでforEachmapなど配列メソッドと組み合わせて使いやすいです。
const keys = Object.keys(person); // キーの配列
const values = Object.values(person); // 値の配列
const entries = Object.entries(person); // [key, value]の配列

スプレッド演算子とオブジェクト(ES2018)

スプレッド演算子(...)は、オブジェクトのプロパティを展開してコピーや結合を簡単に行える機能です。ES2018でオブジェクトにも正式に対応しました。

const obj1 = { a: 1, b: 2 };
const obj2 = { ...obj1, c: 3 }; // { a: 1, b: 2, c: 3 }
  • 既存のオブジェクトを壊さずに新しいオブジェクトを作るため、状態管理や不変性の確保に便利です。
  • 同じキーがあれば後のオブジェクトの値で上書きされます。

オブジェクトの複製

  • シャローコピーは処理が軽く簡単だが、内部のオブジェクトは共有されるため注意が必要。
  • ディープコピーは完全な独立コピーを作るが、処理が重くなりやすく、関数や特殊なデータは扱いに制限がある場合もある。

浅いコピー

シャローコピー(浅いコピー)とは、オブジェクトの最上位のプロパティだけをコピーし、もしプロパティの値が別のオブジェクトや配列だった場合はその参照(ポインタ)をコピーします。つまり、コピー先とコピー元が同じ内部オブジェクトを共有します。

const copy = { ...original };
// または
const copy = Object.assign({}, original);

深いコピー

ディープコピー(深いコピー)は、オブジェクトのすべての階層のプロパティまで再帰的に新しいオブジェクトを作成してコピーします。これにより、コピー元とコピー先は完全に独立した別物になります。

const deepCopy = JSON.parse(JSON.stringify(original));

オブジェクトの凍結と封印

JavaScriptでは、オブジェクトの状態を制御するために「凍結(freeze)」と「封印(seal)」という機能があります。

  • Object.freeze()(凍結) はオブジェクトを完全に固定し、新しいプロパティの追加、既存プロパティの削除や値の変更を禁止します。つまり、オブジェクトの内容を完全に変更不可にします。
  • Object.seal()(封印) はオブジェクトに対して新しいプロパティの追加や削除を禁止しますが、既存のプロパティの値は変更可能です。

これらはオブジェクトの不変性や安全性を高めたい場合に使います。

const obj = { prop: 42 };

Object.freeze(obj); // 変更不可
Object.seal(obj); // プロパティの追加/削除不可(値の変更は可)

プロパティ記述子

JavaScriptのオブジェクトのプロパティには、値だけでなくその振る舞いを制御する「プロパティ記述子(Property Descriptor)」という設定があります。主に以下の属性があります。

  • writable: プロパティの値を書き換え可能かどうか
  • enumerable: for…inObject.keys()などで列挙可能かどうか
  • configurable: プロパティの削除や属性の変更が可能かどうか
  • value: プロパティの値
  • get/set: ゲッター/セッター関数を指定して値の取得・設定時の挙動を制御

Object.defineProperty()メソッドでこれらを詳細に設定でき、細かいアクセス制御や特殊な動作を実装する際に役立ちます。

const obj = {};

Object.defineProperty(obj, 'property', {
  value: 42,
  writable: false, // 書き込み不可
  enumerable: true, // 列挙可能
  configurable: false // 削除/変更不可
});

まとめ

オブジェクトはJavaScriptの基本データ構造で、関連する情報や機能をひとまとめに扱うために欠かせません。作成も操作もシンプルで直感的なので、まずはプロパティの追加、変更、削除、アクセス方法を覚えることが大切です。理解が深まると、より複雑なデータ構造やクラスの基礎にもなります。

演習問題

初級問題(3問)

  1. 次のオブジェクトを作成し、nameプロパティにアクセスするコードを2通りの方法で書きなさい。
   const person = {
     name: '山田太郎',
     age: 30
   };
  1. 次のオブジェクトに新しいプロパティemailを追加しなさい。
   const user = {
     username: 'js_user',
     password: 'secure123'
   };
  1. 次のコードの出力結果は?
   const obj = { a: 1, b: 2 };
   const copy = obj;
   copy.a = 3;
   console.log(obj.a);

中級問題(6問)

  1. 次のオブジェクトのすべてのプロパティ名を配列として取得しなさい。
   const car = {
     make: 'Toyota',
     model: 'Corolla',
     year: 2020
   };
  1. 2つのオブジェクトをマージするコードを書きなさい(スプレッド演算子を使用)。
   const obj1 = { a: 1, b: 2 };
   const obj2 = { b: 3, c: 4 };
  1. 次のオブジェクトからpasswordプロパティを削除しなさい。
   const user = {
     username: 'admin',
     password: 'secret',
     role: 'admin'
   };
  1. 次のオブジェクトのディープコピーを作成するコードを書きなさい。
   const original = {
     name: 'Test',
     nested: {
       value: 10
     }
   };
  1. 次の動作をするcreateUser関数を実装しなさい。
   const user = createUser('太郎', 25);
   console.log(user.name); // '太郎'
   console.log(user.age); // 25
   user.greet(); // 'こんにちは、太郎です!'
  1. 次のコードの出力結果とその理由を説明しなさい。
   const obj = {
     value: 10,
     getValue() {
       return this.value;
     },
     getValueArrow: () => this.value
   };

   console.log(obj.getValue());
   console.log(obj.getValueArrow());

上級問題(3問)

  1. 次のような動作をするcreateImmutableObject関数を実装しなさい。
const person = createImmutableObject({ name: '太郎', age: 30 });
person.name = '花子'; // エラーまたは変更されない 
delete person.age; // エラーまたは削除されない 
person.job = 'エンジニア'; // エラーまたは追加されない
  1. オブジェクトの変更を監視するobserveObject関数を実装しなさい。
const user = { name: '太郎' }; 
const observed = observeObject(user, (prop, oldVal, newVal) => { 
    console.log(`プロパティ ${prop} が ${oldVal} から ${newVal} に変更されました`); 
}); 
observed.name = '花子'; // コンソールに変更を出力 
observed.age = 25; // コンソールに変更を出力
  1. プロキシを使用して、次のような動作をするcreatePrivateProperties関数を実装しなさい。
const obj = createPrivateProperties({ publicProp: '公開プロパティ', _privateProp: '秘密プロパティ' }); 
console.log(obj.publicProp); // '公開プロパティ' 
console.log(obj._privateProp); // undefined

解答例

初級問題解答

  1. 2通りのプロパティアクセス
   const person = {
     name: '山田太郎',
     age: 30
   };

   console.log(person.name);    // ドット記法
   console.log(person['name']); // ブラケット記法
  1. 2通りのプロパティ追加
   const user = {
     username: 'js_user',
     password: 'secure123'
   };

   user.email = 'user@example.com';
   // または
   user['email'] = 'user@example.com';
  1. 出力結果
   3
    const obj = { a: 1, b: 2 };
    const copy = obj;
    copy.a = 3;
    console.log(obj.a); // シャロウコピーの影響を受けるため、3が出力される

中級問題解答

  1. プロパティ名の取得
   const car = {
     make: 'Toyota',
     model: 'Corolla',
     year: 2020
   };
   const properties = Object.keys(car);
  1. オブジェクトのマージ
   const obj1 = { a: 1, b: 2 };
   const obj2 = { b: 3, c: 4 };
   const merged = { ...obj1, ...obj2 }; // { a: 1, b: 3, c: 4 }
  1. プロパティ削除
   const user = {
     username: 'admin',
     password: 'secret',
     role: 'admin'
   };
   delete user.password;
  1. 深いコピー
   const original = {
     name: 'Test',
     nested: {
       value: 10
     }
   };
   const deepCopy = JSON.parse(JSON.stringify(original));
  1. createUser関数
   function createUser(name, age) {
     return {
       name,
       age,
       greet() {
         console.log(`こんにちは、${this.name}です!`);
       }
     };
   }
  1. 出力結果と理由
   10
   undefined
   // アロー関数はthisをレキシカルスコープで解決するため
    const obj = {
        value: 10,
        getValue() {
            return this.value;
        },
        getValueArrow: () => this.value
    };

    console.log(obj.getValue());        // obj.valueの値を取得
    console.log(obj.getValueArrow());   // アロー関数はthisを持たないため、undefinedを返す

上級問題解答

  1. createImmutableObject関数
function createImmutableObject(obj) { 
    return Object.freeze(Object.seal(obj)); 
}
  1. observeObject関数
function observeObject(obj, callback) { 
    return new Proxy(obj, { set(target, prop, value) { const oldVal = target[prop]; target[prop] = value; callback(prop, oldVal, value); return true; } }); 
}
  1. createPrivateProperties関数
javascript function createPrivateProperties(obj) { 
    return new Proxy(obj, { get(target, prop) { if (prop.startsWith('_')) { return undefined; } 
    return target[prop]; }, has(target, prop) { return !prop.startsWith('_') && prop in target; } }); 
}