constは「値」を守る防壁ではない:メモリの深淵で起きていること
JavaScriptを嗜むエンジニアなら、`const`を「定数定義のためのキーワード」と習うはずだ。しかし、実務の最前線で複雑なアプリケーションを構築していると、その認識がどれほどナイーブなものか、痛いほど思い知らされる瞬間がある。
結論から言おう。`const`は「値の不変性(Immutability)」を保証しない。`const`が固定しているのは、変数という名の「メモリアドレス(参照)」だけだ。
この仕様は、JSのメモリモデルを理解し、堅牢なアプリケーションを設計する上で避けて通れない「落とし穴」であると同時に、正しく制御すれば強力な武器にもなる。今日はこの深淵を覗いてみよう。
1. 参照の牢獄とオブジェクトの自由
以下のコードを見てほしい。これが`const`の正体だ。
const appConfig = {
apiEndpoint: ‘https://api.example.com’,
timeout: 5000
};
// これはエラーになる。なぜなら参照先のアドレスを書き換えようとしているからだ。
// appConfig = { new: ‘value’ };
// しかし、これは成功する。
appConfig.timeout = 10000;
console.log(appConfig.timeout); // 10000
`appConfig`という変数は、ヒープ領域にあるオブジェクトの「住所」を保持しているに過ぎない。`const`は「その住所を別の場所に変えるな」と命じているだけだ。オブジェクトの中身(プロパティ)を書き換えることは、住所を変えることとは別次元の操作であるため、JSエンジンはこれを許容する。
これを理解していないと、大規模アプリケーションでは「なぜか意図しないタイミングでデータが書き換わっている」という、デバッグ地獄の入り口に立つことになる。
2. なぜ「ミュータビリティ」は悪魔の誘惑なのか
特にReactのような宣言的UIライブラリや、Reduxのような状態管理を扱う際、この「意図しない書き換え」はパフォーマンスとバグの温床となる。
- レンダリング負荷の増大: 多くのフレームワークは「参照の比較(`===`)」で変更を検知する。オブジェクトを直接書き換えると、参照先が変わらないため、UIが再レンダリングされず、整合性が崩れる。
- 非同期の競合(Race Conditions): 非同期処理の裏側で共有オブジェクトが書き換えられると、後続の処理が予期せぬ状態を参照する。これは再現性の低いバグを量産する。
3. プロフェッショナルのための回避策:防御的プログラミング
では、どうすればこの「不変性」を強制できるのか。現場で採用すべきアプローチはいくつかある。
A. Object.freezeによる硬化
最も素朴な手法だが、深い階層までは凍結できない「浅いフリーズ」であることに注意が必要だ。
const settings = Object.freeze({
theme: ‘dark’,
features: { analytics: true } // ここは凍結されない!
});
settings.theme = ‘light’; // 非厳格モードでは無視、厳格モードではTypeError
settings.features.analytics = false; // 成功してしまう
B. イミュータブルな設計の徹底(推奨)
最近のアーキテクチャでは、オブジェクトを書き換えるのではなく、「常に新しいオブジェクトを生成する」というイミュータブルな設計思想が主流だ。スプレッド構文は、このための強力なツールとなる。
const state = { count: 1, user: { name: ‘Alice’ } };
// 書き換えるのではなく、新しいオブジェクトを作る
const nextState = {
…state,
count: state.count + 1,
user: { …state.user, name: ‘Bob’ }
};
// これにより、参照が異なるため変更検知が確実に機能する
console.log(state !== nextState); // true
4. アーキテクトとしての視点:メモリ効率とのトレードオフ
「毎回新しいオブジェクトを作るのはメモリ効率が悪いのでは?」という懸念を持つかもしれない。確かに、小規模なスクリプトならそうだろう。しかし、現代のJSエンジン(V8など)は、小規模なオブジェクトの生成と破棄に関しては極めて高度に最適化されている。
むしろ、「副作用によるバグを特定するために費やす数時間のデバッグコスト」を考慮すれば、イミュータブルな設計によるメモリ負荷の増加など微々たるものだ。
最後に:不変性をコードの「信頼」にする
堅牢なWebアプリケーションとは、予測可能なアプリケーションのことだ。`const`を過信せず、「この変数は本当に変更されるべきか?」と自問自答すること。そして、可能であればTypeScriptの `readonly` や `Readonly
JavaScriptは柔軟だが、その柔軟性は使い手を殺し得る諸刃の剣だ。`const`が持つ「参照の固定」という限定的な特性を理解し、あえて「不変の壁」を自分たちで構築する。それこそが、伝説的なコードを生むエンジニアの流儀であると私は信じている。
さて、君のプロジェクトにあるその共有オブジェクト、本当に「`const`なら安全」と言い切れるかな?

コメント