やあ。コードベースの海を泳いでいると、「`const` を使っているからこのオブジェクトは安全だ」という誤解に、何度出くわしたことか。
中堅どころのエンジニアが、レビューで「ここ、`const` なのに書き換えられちゃってますよ?」と指摘されているシーンをよく見かける。だが、実はその指摘こそが「`const` の本質」を履き違えているケースが多いんだ。
今日は、JavaScriptという言語の少しばかり「泥臭い」仕様と、現場で生き残るための知見について話をしよう。
—
1. constは「値の監獄」ではない、「住所の固定」だ
多くの初心者が `const` を「定数(Constant)」と直訳して、「値そのものを変えられない魔法の呪文」だと勘違いする。だが、JavaScriptのエンジン(V8など)が裏で何をしているかを覗けば、真実が見えてくる。
`const` は、「メモリ上の特定の住所(参照先)を書き換えさせない」という契約に過ぎない。
例えば、オブジェクトを代入したとき、変数に入っているのはオブジェクトそのものではなく、ヒープ領域にあるオブジェクトの「住所(参照)」だ。`const` は、その「住所」を別のものに差し替えることを禁止しているだけで、住所の先にある「家の中身(プロパティ)」をリフォームすることまでは制限していない。
const user = {
name: ‘Taro’,
role: ‘developer’
};
// これはOK。参照先(住所)は変わっていないから。
user.role = ‘architect’;
// これはNG。別の住所(別のオブジェクト)を代入しようとしているから。
// Uncaught TypeError: Assignment to constant variable.
user = { name: ‘Jiro’ };
これがJavaScriptの仕様だ。ブラウザのエンジンは、`const` で宣言された変数が再代入されようとした瞬間に、強力なガードをかけてエラーを投げる。だが、オブジェクトの内部プロパティにアクセスする際は、そのガードは発動しない。
—
2. 現場で直面する「バグの温床」
この仕様、一見すると便利だが、大規模開発では「意図しない副作用」の温床になる。
例えば、Reactのステートや、Reduxのストア、あるいはAPIから受け取った設定オブジェクトを、関数の引数として渡して「なんとなく」変更してしまうケースだ。コードのどこかで「参照」が共有されていると、ある場所での変更が、別の場所で予期せぬ挙動を引き起こす。
「あれ、さっきまで `name` は Taro だったはずなのに、なぜか Jiro に変わっている……?」
デバッグに数時間を費やすことになる、典型的なパターンだ。
—
3. 実践:どう防衛するか?(ベストプラクティス)
「`const` だから安心」と祈るのではなく、「変更される前提で防御する」のが、歴戦のエンジニアの流儀だ。現場ですぐ使えるTipsをいくつか紹介しよう。
方法A:Object.freeze() で物理的に封印する
オブジェクトのミュータビリティを真に制限したいなら、`Object.freeze()` を使う。これはオブジェクト自体を「変更不能」にする。
const config = Object.freeze({
apiEndpoint: ‘https://api.example.com’,
timeout: 5000
});
// 厳格モード(use strict)では、ここでエラーが発生して書き換えを阻止できる
config.timeout = 10000;
console.log(config.timeout); // 5000のまま
※注意:これは「浅い(Shallow)」凍結だ。ネストされたオブジェクトの中身までは凍結できない点に注意が必要だ。
方法B:イミュータブルな更新(Spread Operatorの活用)
モダンな開発なら、オブジェクトを直接いじるのは御法度だ。新しいオブジェクトを作成して、変更したい部分だけ差し替える。これがReact開発などで求められる「イミュータブルな考え方」の基本だ。
const originalUser = { name: ‘Taro’, role: ‘developer’ };
// 元のオブジェクトを壊さず、新しいオブジェクトを作成する
const updatedUser = {
…originalUser,
role: ‘architect’
};
console.log(originalUser.role); // ‘developer’ (元のオブジェクトは無事)
console.log(updatedUser.role); // ‘architect’ (新しいオブジェクトが生成された)
—
最後に:賢いエンジニアは「意図」をコードにする
結局のところ、`const` を使うか、`Object.freeze` を使うか、あるいはイミュータブルに更新するかは、「そのコードをどう扱ってほしいか」という意思表示だ。
- 本当に再代入させたくないなら `const`。
- 絶対に中身を改ざんさせたくない設定値なら `Object.freeze`。
- データの流れを追跡しやすくしたいなら、常に新しいオブジェクトを生成する。
JavaScriptという言語は、非常に自由で甘やかしてくれる。だからこそ、私たちエンジニアが「制約」を意識的に設計してやらなければならない。
次に誰かが君の書いたコードを読んだとき、「ああ、ここは絶対に書き換わらないように設計されているんだな」と、その意図が伝わるように書こう。それができるようになったとき、君のコードは中級者から「信頼できるアーキテクト」のコードへと昇華されるはずだ。
さて、エディタに戻ろう。やるべきことはまだたくさんあるはずだ。

コメント