JavaScriptのObject.freezeの使い方!凍結して変更不可にする

JavaScriptのObject.freezeメソッドは、オブジェクトを凍結(freeze)するために使用されます。
オブジェクトを凍結すると、そのオブジェクトは変更不可能になります。

具体的には、以下の操作ができなくなります。

・既存のプロパティの値の変更
・新しいプロパティの追加
・既存のプロパティの削除
・既存のプロパティの属性(`writable`, `configurable` など)の変更
・オブジェクトのプロトタイプを変更すること

これにより、オブジェクトが意図せず変更されるのを防ぎ、コードの堅牢性を高めることができます。
特に、設定オブジェクト、定数データ、共有される読み取り専用データなどを扱う場合に非常に役立ちます。

この記事では、Object.freezeメソッドの基本的な使い方、引数、戻り値、主要な使用例、そして使用する際の重要な注意点について解説します。

Object.freezeメソッドの基本的な構文

Object.freeze() メソッドの基本的な構文は以下の通りです。

Object.freeze(obj)
  • obj: 必須。凍結したいオブジェクトです。

Object.freezeの引数(obj)

  • 必須です。
  • 凍結したいオブジェクトを指定します。
  • objがプリミティブ値(数値、文字列、真偽値、nullundefinedなど)の場合、それらはそもそも変更不可能であるため、Object.freezeを適用しても何も起こらず、そのままの値が返されます。TypeErrorは発生しません。

Object.freezeの戻り値

凍結されたオブジェクトが返されます。

Object.freezeメソッドを使ってみる

Object.freezeを実際に使って、動作を確認する。

例1:基本的なオブジェクトの凍結

Object.freezeを使用して、オブジェクトのプロパティを変更したり、追加・削除したりできなくします。

const user = {
  name: 'Alice',
  age: 30
};

Object.freeze(user); // userオブジェクトを凍結

// プロパティの変更を試みる
user.age = 31; // 変更されない (厳格モードではTypeError)
user.name = 'Bob'; // 変更されない

// 新しいプロパティの追加を試みる
user.city = 'New York'; // 追加されない (厳格モードではTypeError)

// プロパティの削除を試みる
delete user.age; // 削除されない (厳格モードではTypeError)

console.log(user); // 出力: { name: 'Alice', age: 30 }

最初にuserオブジェクトを作って、そのオブジェクトをObject.freezeメソッドに渡しました。

プロパティを変更して、最後に出力しようとしています。
上記のコードを実行すると、プロパティを変更しようとしていますが、変わっていないことが確認できます。(非厳格モード)

厳格モード ("use strict"; をファイルの先頭や関数の先頭に記述) で実行すると、これらの変更操作は TypeError をスローします。
非厳格モードでは、サイレントに失敗します。(エラーは発生しないが、変更もされない)

例2:配列の凍結

配列もオブジェクトの一種なので、Object.freezeを適用できます。
これにより、配列の要素の変更、追加、削除ができなくなります。

// 非厳格モードでの挙動を確認するため、"use strict"; はここでは記述しません。
// ただし、ブラウザのコンソールやNode.jsのモジュールではデフォルトで厳格モードの場合があります。

const colors = ['red', 'green', 'blue'];

Object.freeze(colors); // colors配列を凍結

console.log("--- 配列の凍結後の操作 ---");

// 要素の変更を試みる
try {
  colors[0] = 'orange'; // 非厳格モードでもサイレントに失敗(変更されない)
                        // 厳格モードではTypeError
} catch (e) {
  console.error("エラー: 要素の変更はできません。", e.message);
}
console.log(`colors[0] after attempt to change: ${colors[0]}`); // 出力: red (変更されていないことを確認)

// 要素の追加を試みる (pushメソッド)
try {
  // Object.freezeで拡張不可能になっているため、pushは新しいプロパティを追加できずTypeErrorをスローします。
  colors.push('yellow'); // 非厳格モードでもTypeErrorが発生
} catch (e) {
  console.error("エラー: 要素の追加はできません。", e.message);
}
console.log(`colors.length after push attempt: ${colors.length}`); // 出力: 3 (追加されていないことを確認)
console.log(`colors after push attempt: `, colors); // 出力: [ 'red', 'green', 'blue' ]

// 要素の削除を試みる (delete演算子)
// 非厳格モードでは、configurable: false のプロパティの削除はサイレントに失敗し、falseを返します。
const deleteResult = delete colors[1];
console.log(`delete colors[1] result (non-strict): ${deleteResult}`); // 出力: false
console.log(`colors after delete attempt: `, colors); // 出力: [ 'red', 'green', 'blue' ] (削除されていないことを確認)

// 厳格モードで実行した場合のコメントアウトされた例:
/*
"use strict";
const strictColors = ['red', 'green', 'blue'];
Object.freeze(strictColors);
try { strictColors.push('yellow'); } catch (e) { console.error("Strict Mode Push Error:", e.message); } // TypeError
try { delete strictColors[1]; } catch (e) { console.error("Strict Mode Delete Error:", e.message); } // TypeError
*/

配列を凍結すると、その配列は固定長になり、要素も変更できなくなります。

colors配列を作成して、Object.freezeで配列を凍結しています。
次の処理でcolors配列に対して、要素を書き換えようとしますが、変更されません。

またpushメソッドは、凍結された配列に対して呼び出されると、非厳格モードでもTypeErrorをスローします。
これは、配列が拡張不可能になったため、新しいプロパティを追加できないためです。

一方、delete演算子によるプロパティの削除は、非厳格モードではサイレントに失敗します。(エラーはスローせず、false を返します)

例3:シャローフリーズの挙動(ネストされたオブジェクト)

Object.freezeはシャローフリーズであるため、ネストされたオブジェクトのプロパティは変更可能です。

"use strict"; // 厳格モードを有効にする (この例では直接エラーは発生しないが、推奨)

const company = {
  name: 'Tech Corp',
  address: {
    street: '123 Main St',
    city: 'Anytown'
  },
  departments: ['Sales', 'Marketing']
};

Object.freeze(company); // companyオブジェクトを凍結

// company自身のプロパティは変更不可
try {
  company.name = 'New Tech Corp'; // 厳格モードではTypeErrorが発生
} catch (e) {
  console.error("エラー: company.nameの変更はできません。", e.message);
}
console.log(company.name); // 出力: Tech Corp

// ネストされたオブジェクトのプロパティは変更可能
company.address.city = 'New City'; // 変更される
console.log(company.address.city); // 出力: New City

// ネストされた配列の要素も変更可能
company.departments.push('HR'); // 追加される
console.log(company.departments); // 出力: [ 'Sales', 'Marketing', 'HR' ]

console.log(company);
/*
出力:
{
  name: 'Tech Corp',
  address: { street: '123 Main St', city: 'New City' }, // cityが変更されている
  departments: [ 'Sales', 'Marketing', 'HR' ] // HRが追加されている
}
*/

companyオブジェクトを作成して、Object.freezeで凍結しています。

次の処理でプロパティを変更しようとしていますが、凍結されているため変更できません。
一方、ネストが深くなったcompany.address.cityや、配列のcompany.departmentsは変更可能です。

この例では、Object.freezeが直接のプロパティのみを凍結し、ネストされたオブジェクトや配列の内部は凍結しないことを明確に示しています。
ネストされたオブジェクトも凍結したい場合は、再帰的にObject.freezeを適用するディープフリーズの処理を実装する必要があります。

Object.freezeを使う際の重要な注意点

Object.freezeを使う際の注意点です。

シャローフリーズであること

最も重要な注意点です。ネストされたオブジェクトや配列は凍結されません。
完全に変更不可能なオブジェクト(ディープイミュータブル)が必要な場合は、再帰的にObject.freezeを適用するか、Immutable.jsのようなライブラリの使用を検討してください。

元のオブジェクトが変更される(破壊的)

Object.freezeは、引数として渡されたオブジェクトを直接変更し、そのオブジェクトが凍結されます。

凍結は解除できない

一度凍結されたオブジェクトは、JavaScriptの標準機能では凍結を解除できません
凍結する前に、本当に変更不可能にして良いか慎重に検討してください。

厳格モードでの挙動

厳格モード ("use strict") では、凍結されたオブジェクトに対する変更操作(値の変更、追加、削除など)は TypeError をスローします。
非厳格モードではサイレントに失敗します。(エラーは発生しないが、変更もされない)
本番環境では通常厳格モードを使用するため、エラーが発生することに注意してください。

プロトタイプチェーン上のプロパティ

Object.freezeは、オブジェクト自身のプロパティのみを凍結します。
プロトタイプチェーンから継承されたプロパティは凍結されません。

まとめ

JavaScriptのObject.freezeメソッドは、オブジェクトを凍結して変更不可能にするための強力なツールです。

設定オブジェクト、定数データ、共有される読み取り専用データなど、意図しない変更から保護したい場合に非常に便利です。
しかし、シャローフリーズであること、一度凍結すると解除できないこと、そして厳格モードでのエラー挙動という重要な注意点を理解して使用することが不可欠です。

これらのポイントを踏まえ、Object.freezeを効果的に活用し、JavaScriptアプリケーションの堅牢性と信頼性を高めましょう。

コメント