【入門編】プロトタイプ汚染を防ぐための「凍結」テクニック:Object.freezeとconstの併用 – JavaScriptコア文法・モダン言語仕様と変数・関数解析バイブル

こんにちは!フロントエンドからNode.jsの深層まで、JavaScriptの生態系を隅々まで愛するフルスタックエンジニアの先輩です。

今回は、JavaScriptの変数とオブジェクトの裏側にある「本当の防御力」についてお話ししますね。ここをクリアすれば、あなたの書くコードは一気にプロダクションクオリティ(実戦レベル)に近づきますよ。

—

1. `const` は「オブジェクトの中身」を守れないという事実

JavaScriptを学び始めると、変数の宣言には `const` を使おう!と教わりますよね。これは非常に正しいベストプラクティスです。なぜなら、うっかり再代入(値の書き換え)をしてバグを生むのを防いでくれるからです。

例えば、こんなコードを書いたとします。

const config = {
theme: ‘dark’,
version: 1.0
};

// これはエラーになる(再代入の禁止)
// config = { theme: ‘light’, version: 2.0 };

「よし、`const` を使ったから `config` は安全だ!」……そう思いませんでしたか?
実はここに、JavaScript初学者が最もハマりやすい大きな罠があります。

実は、`const` が守ってくれるのは「変数の箱(アドレス)」だけなんです。箱の中に入っている「オブジェクトの実体」は、なんと外側から自由自在に書き換えることができてしまいます。

const config = {
theme: ‘dark’,
version: 1.0
};

// オブジェクトのプロパティは書き換えられてしまう!
config.theme = ‘light’;
config.newFeature = true; // 勝手に追加もできる

console.log(config);
// 出力: { theme: ‘light’, version: 1, newFeature: true }

あれれ? `const` を使ったのに、中身が書き換わってしまいましたね。
これは、変数 `config` に格納されているのが「オブジェクトそのもの」ではなく、「オブジェクトがメモリ上のどこにいるか」という参照(メモリアドレス)だからです。`const` はその「住所」の変更を禁止しているだけで、住所の先にある「家(オブジェクト)」の壁を塗り替えたり、勝手に増築したりすることまでは止められないのです。

—

2. そこで登場するのが `Object.freeze()` です

アプリケーションの共通設定や、絶対に書き換わって欲しくない定数オブジェクトを扱うとき、「勝手に書き換えられたくない!」と思いますよね。

そこで登場するのが、V8エンジンレベルでオブジェクトを完全にロックダウンする魔法のメソッド、`Object.freeze()` です。

これを使うと、オブジェクトのプロパティの追加、削除、変更が一切できなくなります(厳格モード=strict modeでは、書き換えようとするとエラーが発生します)。

基本的な使い方

‘use strict’; // 厳格モードを有効化

const APP_CONFIG = Object.freeze({
API_ENDPOINT: ‘https://api.example.com/v1’,
TIMEOUT: 5000,
retries: 3
});

// プロパティを変更しようとしてみる
APP_CONFIG.TIMEOUT = 10000; // ⚠️ここでTypeErrorが発生!(または無視される)

// プロパティを追加しようとしてみる
APP_CONFIG.apiKey = ‘secret_123’;

console.log(APP_CONFIG);
// 出力結果(変更は一切反映されない):
// { API_ENDPOINT: ‘https://api.example.com/v1’, TIMEOUT: 5000, retries: 3 }

`const` と `Object.freeze()` をタッグを組ませることで、「変数への再代入の禁止(`const`)」と「オブジェクトの中身の改ざん禁止(`Object.freeze`)」の両方を完璧に達成できるというわけです。これが、モダンJSにおける堅牢なコーディングの基本スタイルになります。

—

3. ここに注意!`Object.freeze()` の「シャロー(浅い)凍結」の罠

さて、ここで少し踏み込んだ話をします。
実は `Object.freeze()` には、実務でよく見落とされる重大な特性があります。それは、「凍結するのは直下の階層だけ(シャロー・フリーズ)」という点です。

もしオブジェクトの中に、さらに別のオブジェクト(入れ子構造)が含まれている場合、内側のオブジェクトは凍結されません。

‘use strict’;

const user = Object.freeze({
name: ‘Taro’,
settings: {
theme: ‘dark’ // ここはネスト(入れ子)されたオブジェクト
}
});

// 外側は凍結されているので変更できない
user.name = ‘Jiro’;

// しかし!内側のオブジェクトは凍結されていないので変更できてしまう!
user.settings.theme = ‘light’;

console.log(user.settings.theme);
// 出力: ‘light’ (書き換わってしまっている!)

おっと、これは怖いですね。完全な防御的プログラミングを目指すなら、ネストされたオブジェクト(多次元オブジェクト)の末端まで再帰的に凍結させる必要があります。

実務で使える「ディープ・フリーズ(深層凍結)」の関数

もし実務で完全なイミュータブル(不変)オブジェクトを作りたい場合は、以下のようなヘルパー関数を用意しておくと非常に重宝します。

/

  • オブジェクトを再帰的に凍結するディープフリーズ関数
  • @param {Object} obj 凍結したいオブジェクト
  • @returns {Object} 凍結されたオブジェクト

/
function deepFreeze(obj) {
// オブジェクトのプロパティ名をすべて取得
const propNames = Object.getOwnPropertyNames(obj);

// プロパティが持つオブジェクトもそれぞれ凍結する
for (let name of propNames) {
const value = obj[name];

// 値がオブジェクト(かつnullではない)場合、再帰的にfreezeする
if (value && (typeof value === ‘object’ || typeof value === ‘function’)) {
deepFreeze(value);
}
}

return Object.freeze(obj);
}

// 使用例
const appSettings = deepFreeze({
env: ‘production’,
database: {
host: ‘localhost’,
port: 3306
}
});

// これでネストされた内部プロパティも完全に守られる!
// appSettings.database.port = 5432; // エラーになるか、変更されない

—

まとめ:ここをクリアすれば、あなたのコードはプロの領域へ

今回は、変数の宣言とオブジェクトの保護について、少し裏側の仕組みも含めて解説しました。

  • `const` は「変数の再代入」を防ぐ。
  • `Object.freeze()` は「オブジェクトのプロパティの改変」を防ぐ。
  • ネストされたオブジェクトには再帰的なアプローチ(ディープフリーズ)が必要。

この2つを組み合わせる意識を持つだけで、意図しないバグやセキュリティ上の脆弱性(プロトタイプ汚染など)をガラッと減らすことができます。

ここをしっかりと理解できたあなたは、もうただの初学者ではありません。堅牢で美しいコードを書くための確かな一歩を踏み出していますよ。明日からのコーディングでぜひ試してみてくださいね!

タイトルとURLをコピーしました