Object.freeze()は1階層しか凍結しない(浅い凍結)
Object.freeze()はオブジェクト自身の直下のプロパティの再代入・追加・削除だけを禁止します。プロパティの値がオブジェクトである場合、そのネストしたオブジェクトは凍結されず、自由に変更できてしまいます。
なぜエラーが出ないのか
出力: true
(エラーなし)- JavaScriptは何も報告しません。文法として正しいためです
出力: true- 実際の挙動 — 期待した結果と食い違っている箇所
見つけ方- エラーが出ないので、出力を目で確かめるか、コードを目で追うしかありません。この種の誤りが最も発見が遅れます
このエラーが出る典型パターン
パターン1
1 function isDeeplyFrozen(obj) { 2 return Object.isFrozen(obj) && Object.isFrozen(config ); ^ 3 } 4 const config = Object.freeze({ name: "app", meta: { version: 1 } }); 5 console.log(isDeeplyFrozen(config));
出力: true期待: false (エラーにはなりません)
Object.freezeは直下のプロパティしか凍結しません。configは凍結されていても、ネストしたmetaは凍結されていないため、isFrozenチェックがすり抜けます。
直し方: config を config.meta にします。
広告
広告スロット(未設定)
パターン2
1 function isDeeplyFrozen(obj) { 2 return Object.isFrozen(obj) && Object.isFrozen(settings ); ^ 3 } 4 const settings = Object.freeze({ theme: "dark", limits: { max: 10 } }); 5 console.log(isDeeplyFrozen(settings));
出力: true期待: false (エラーにはなりません)
settings自体は凍結されていますが、ネストしたlimitsオブジェクトは凍結の対象外です。
直し方: settings を settings.limits にします。
パターン3
1 function isDeeplyFrozen(obj) { 2 return Object.isFrozen(obj) && Object.isFrozen(profile ); ^ 3 } 4 const profile = Object.freeze({ name: "Al", social: { twitter: "@al" } }); 5 console.log(isDeeplyFrozen(profile));
出力: true期待: false (エラーにはなりません)
profileは凍結されていても、ネストしたsocialオブジェクトは別物として扱われ、凍結されていません。
直し方: profile を profile.social にします。
よくある誤解
「freezeしたオブジェクトはまるごと不変になるはず」という思い込みは誤りです。凍結されるのは最上位の階層だけで、ネストした値まで凍結したい場合は再帰的に凍結する処理が別途必要です。
まとめ
Object.freeze()は1階層しか凍結しない(浅い凍結)は上級でつまずきやすい項目です。上の3パターンを実際に手で直すと、エラーメッセージのどこを読めばよいかが掴めます。