「const は定数だから、一度決めたら絶対に変更できない」と思っていませんか?
const が保証するのは 「再代入の禁止」 だけです。中身が オブジェクト(連想配列)や配列 の場合、その中身を変更することは可能です。
【実験1】(プロパティの変更)はエラーにならずに実行されますが、【実験2】(appSettings = ...)を実行するとどうなると思いますか?
- 実験1と同じく、問題なく変更される
- ここでエラーが発生する
const は「変数と値の紐付け(バインディング)」をロックするものであり、値そのものを不変にするわけではない、という点が非常に重要です。
- 再代入(
=): 変数が指す「場所(参照先)」を変えようとする行為 → NG ❌ - プロパティ変更: 参照先の「中身」を書き換える行為 → OK ⭕
真の不変性(イミュータブル)を求めて
「const でも中身が変わってしまうなら、完全に変更できないオブジェクトを作りたいときはどうするの?」と思いますよね。堅牢な設計では、意図しない書き換えを防ぐためにオブジェクトを凍結させることがあります。
Object.freeze() は、オブジェクトを「凍結」して、プロパティの追加・削除・変更を一切禁止にする強力なメソッドです。これで const と組み合わせることで、かなり堅牢な定数オブジェクトを作ることができます。
しかし、ここにも重要な「落とし穴」が一つだけあります。それは 「浅い凍結(Shallow Freeze)」 という性質です。
この性質を理解するために、以下のコードを見てみましょう。Object.freeze() をしたはずなのに、直感に反する挙動をする場所があります。
「実験2(ネストされた timeouts.connection)」の値はどうなると思いますか?
5000のまま変わらない(凍結されているから)99999に変更されてしまう
これは 「浅い凍結(Shallow Freeze)」 と呼ばれる現象です。Object.freeze() は、対象のオブジェクト(ここでは serverConfig)の直下のプロパティだけをロックします。
serverConfigの箱: 鍵がかかっています。timeoutsプロパティ: 箱の中に「別の箱(オブジェクト)への地図(参照)」が入っています。この地図自体は書き換えられませんが、地図が指し示す先の箱には鍵がかかっていません。 そのため、中身を自由に変更できてしまったのです。
Tips: 完全に凍結する「Deep Freeze」
実務で「完全に変更不可能な設定オブジェクト」を作りたい場合は、再帰処理を使ってすべての階層を凍結する必要があります。
共有コメント 共有されるコメント欄です。