【入門編】constによる参照の固定と「不変性」の誤解:オブジェクトのプロパティ変更が隠しクラスに与える影響 – JavaScriptコア文法・モダン言語仕様と変数・関数解析バイブル

こんにちは!普段はフロントエンドからNode.jsの低レイヤー、そしてV8エンジンのソースコードやメモリプロファイルと日夜格闘しているチーフアーキテクトです。

JavaScriptを学び始めると、変数の宣言に `const` を使うことが推奨される場面にたくさん出会いますよね。「値を変えないなら `const`」と教わった方も多いはずです。でも、こんな疑問を持ったことはありませんか?

「あれ? `const` で宣言したオブジェクトなのに、中のプロパティは書き換えられちゃうんだけど……これってバグじゃないの?」

実はこれ、JavaScript初学者だけでなく、他のプログラミング言語からやってきた開発者も必ずと言っていいほどハマる「罠」なんです。ここをクリアできるかどうかが、JavaScriptのメモリ管理やオブジェクトの挙動を完全にマスターできるかどうかの分かれ道になります。

今回は、V8エンジンの裏側の動きまで少しだけ覗きながら、`const` の正体と、モダンなJavaScript開発で求められる「真の不変性(イミュータブル)」の設計思想を一緒に解き明かしていきましょう!ここをクリアすれば、あなたのJavaScriptの基本はバッチリマスターできますよ。

—

1. `const` が守っているのは「中身」ではなく「住所」

まずは基本のおさらいからいきましょう。`const` は “constant(定数)” の略です。そのため、「`const` で宣言した変数は、一度代入したら二度と内容を変更できない」と思われがちです。

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

const score = 100;
// score = 200; // ここで再代入しようとすると…
// ➡️ TypeError: Assignment to constant variable.(エラーになります!)

これは数値(プリミティブ型)なので、再代入しようとするとV8エンジンが怒ってエラーを出してくれます。ここまではバッチリですよね。

問題は、オブジェクトや配列を代入したときです。

const player = {
name: ‘勇者’,
level: 1
};

// 中身のプロパティを変更してみる
player.level = 2;
console.log(player.level); // ➡️ 2 (あれ?エラーにならないし、変わった!)

「あれっ? `const` なのに値が変わったぞ!?」と驚きますよね。
ここで、JavaScriptがメモリ上でどう動いているのか、ちょっとイメージ図で考えてみましょう。

[変数 player] ──(参照・矢印)──> [メモリ上のオブジェクト { name: ‘勇者’, level: 1 }]

JavaScriptにおいて、変数(この場合は `player`)が持っているのは、オブジェクトの「実データそのもの」ではありません。オブジェクトが置いてある「メモリ上の住所(参照)」なんです。

`const` が厳格に守っているのは、この「住所の書き換え禁止」です。つまり、`player` という変数に、別のオブジェクトの住所を上書きしようとするとエラーになります。

const player = { name: ‘勇者’ };

// これはNG(住所の再代入なのでエラー)
player = { name: ‘魔法使い’ };
// ➡️ TypeError: Assignment to constant variable.

しかし、住所そのものは変えずに、「その住所にある家(オブジェクトのプロパティ)のリフォームをする」ことは、`const` の管轄外なのです。これが、`const` が「参照の不変性」しか担保しないと言われる理由なんですね。

—

2. オブジェクトの書き換えがV8エンジンに与える影響

「なるほど、住所が同じなら中身を変えてもいいんだな」と思ったそこのあなた。ちょっと待ってください。実務の現場やパフォーマンスを意識したコードを書く上では、ここからが一番重要なポイントです。

JavaScriptを実行しているV8エンジン(ChromeやNode.jsの心臓部)は、コードを爆速で動かすために「隠しクラス(Hidden Class / Maps)」という独自の最適化の仕組みを持っています。

V8は、オブジェクトが「どんなプロパティを持っているか」を監視しています。例えば、次のようなコードがあったとします。

const user = { id: 1, name: ‘Taro’ };

このとき、V8は「`id` と `name` を持つオブジェクト用の設計図(隠しクラス)」をメモリ上に作成し、高速にアクセスできるように最適化します。

ここに、後から新しいプロパティをポロッと追加したりするとどうなるでしょうか?

user.age = 25; // 後からプロパティを追加

V8エンジンからすると、「おっと、急に設計図にない部屋が追加されたぞ。今の最適化された設計図が使えなくなったから、新しい設計図を作り直さなきゃ……!」と、エンジンの内部で再最適化(トランジション)のコストが発生してしまいます。

回数が少なければ問題ありませんが、ループの中で何千回もオブジェクトの形(構造)をダイナミックに変え続けるようなコードを書くと、V8の最適化が台無しになり、ガベージコレクション(メモリのお掃除)も頻発してアプリケーション全体の動作が重くなってしまいます。

—

3. 本当の意味で「不変(イミュータブル)」なコードを書くために

ここまでお話しすると、「じゃあどうすればいいの?」という疑問が湧きますよね。
モダンなJavaScript / フロントエンド開発(Reactなど)では、オブジェクトのプロパティを直接書き換える(ミュータブルな操作)のを避け、「新しいオブジェクトを丸ごと作り直す」というイミュータブルな設計思想が主流になっています。

ここで大活躍するのが、ES6で導入されたスプレッド構文(`…`)です。

悪い例:直接書き換える(ミュータブル)

const state = { count: 1, text: ‘Hello’ };

// 直接書き換え(V8の最適化にも優しくなく、どこでデータが変わったか追いにくい)
state.count = 2;

良い例:スプレッド構文で新しく作り直す(イミュータブル)

const state = { count: 1, text: ‘Hello’ };

// 古いstateを展開して、countだけを書き換えた「まったく新しいオブジェクト」を作る
const nextState = {
…state,
count: 2
};

console.log(state.count); // ➡️ 1 (元のデータは汚染されていない!)
console.log(nextState.count); // ➡️ 2 (新しいデータ)

このように、データを直接いじらずにコピーして新しいものを作るアプローチをとることで、プログラムの予測可能性が劇的に上がり、「あれ?どこでこのデータ書き換わったんだっけ……?」というデバッグ地獄から解放されるようになります。

—

4. さらにガチガチに固めたいときは:`Object.freeze()`

「いやいや、うっかり書き換えてしまうミスをコードレベルで完全に防ぎたいんだ!」という場合は、JavaScriptの組み込み関数である `Object.freeze()` を使うのが有効です。

const config = Object.freeze({
apiEndpoint: ‘https://api.example.com’,
timeout: 5000
});

// プロパティを変更しようとしてみる
config.timeout = 10000;

console.log(config.timeout);
// ➡️ 5000 (エラーにはならないことが多いですが、値は変わりません! ※厳格モード ‘use strict’ の場合はTypeErrorになります)

ただし、注意点があります。`Object.freeze()` は「浅い凍結(Shallow Freeze)」なので、オブジェクトの中にさらに別のオブジェクトが入っている場合、ネストされた奥のオブジェクトまでは凍結してくれません。完全な不変性を求める場合は、専用のライブラリ(Immerなど)を使うか、再帰的に凍結する処理を書く必要があります。

—

まとめ

今回の極限の知見をギュッと凝縮して振り返ってみましょう!

1. `const` が保証するのは「値の不変性」ではなく、「変数アサイン(住所)の固定」である。
2. オブジェクトのプロパティは `const` であっても変更可能だが、安易な構造変更は V8エンジンの最適化(隠しクラス)の足を引っ張る可能性がある。
3. 実務やモダンなフレームワークでは、スプレッド構文(`…`)などを使って「新しいオブジェクトを作り直す(イミュータブルな設計)」のが王道。

この基本原則が頭に入っていれば、大規模なフロントエンドアプリケーションを構築する際も、メモリ効率が良くバグの少ない、洗練されたコードを書き続けることができますよ。

さあ、今日の学びを活かして、あなたのコードもより美しく、より堅牢なものにアップデートしていきましょう!質問や感想があれば、ぜひコメント欄で教えてくださいね。

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