概要
JavaScriptのバグの多くは、「何が渡されて、何が変わったのか」が見えていないところから始まる。
その本質にあるのが、プリミティブ型(値を直接渡す)と参照型(実体の参照という値を渡す) の違いだ。
この理解が曖昧なままコードを書くと、オブジェクトが意図せず書き換わったり、配列が破壊されたりする。
意図しない副作用が“静かに”発生する、最も根深いバグ要因の一つである。
この記事では、プリミティブと参照型の本質的な違いを明確にし、実務でどう活かすかまでを徹底的に掘り下げる。
補足
※ 厳密には、JavaScriptはすべて「値渡し(pass by value)」です。
オブジェクトや配列は「参照を値として渡す」ため、関数内で状態が変化するように見えるだけです。
本記事では便宜上「参照渡し」と表現している箇所がありますが、正確には「同じ参照が渡っている」と読み替えてください。
対象環境
モダンブラウザ対応のJavaScript(ES6〜)
Node.js含む全実行環境
そもそも「プリミティブ型」とは何か?
プリミティブ型(Primitive)は、値そのものを扱う型。
✅ 対象となる型:
string
number
boolean
null
undefined
symbol
bigint
特徴:
- 値そのものを保持・渡す
- 変更不可(イミュータブル)
- コピーすれば完全に分離された値になる
let a = 'hello';
let b = a;
b = 'world';
console.log(a); // 'hello'(変更されていない)
→ a
と b
は完全に独立した値
「参照型」とは何か?
参照型(Reference)は、実体へのポインタを扱う型。
✅ 対象となる型:
object
array
function
特徴:
- 実体はヒープに格納され、変数はその参照(ポインタ) を持つ
- コピーしても同じものを指している
- 一方を変更するともう一方も変わる
let obj1 = { name: 'Alice' };
let obj2 = obj1;
obj2.name = 'Bob';
console.log(obj1.name); // 'Bob'(obj1 も書き換わっている)
→ これはobj1 と obj2 が同じ場所を見ているため
破壊的コードと非破壊的コードの差
// 破壊的(mutate)
const list = [1, 2, 3];
list.push(4); // 変更される
// 非破壊的(immutable)
const newList = [...list, 4]; // listは変わらず、新しい配列を返す
→ React / Vue / Redux など、状態管理が重要な設計では「非破壊」が必須
→ 参照型をそのまま操作すると、予期せぬ状態変化が発生する
関数渡しの落とし穴
function mutate(obj) {
obj.value = 100;
}
const data = { value: 0 };
mutate(data);
console.log(data.value); // 100 ← 副作用が発生
→ 参照型を引数に渡すと、関数内で外部の状態を破壊できる
✅ 対策:構造をコピーして渡す
function safeMutate(obj) {
const copy = { ...obj };
copy.value = 100;
return copy;
}
const result = safeMutate(data);
console.log(data.value); // 0
console.log(result.value); // 100
→ 安全な状態変更には、ディープコピー or シャローコピー + 再代入が有効
比較の罠:プリミティブと参照型の ===
'hello' === 'hello' // true
{} === {} // false
[] === [] // false
→ 参照型は“中身”ではなく“場所”が違えば false
実務Tips:参照型と付き合う4つの原則
- 引数でオブジェクトや配列を受け取る関数は、副作用を避ける
- 再利用される変数にはシャローコピー / ディープコピーを必ず検討
- 状態管理ではイミュータブルなパターン(スプレッド演算子など)を徹底
- 参照型を比較するときは、参照の一致か、値の比較かを意識する
結語
JavaScriptは「すべてがオブジェクト」という呪文のもと、プリミティブと参照型が曖昧に扱われがちだ。
だが、バグの原因の多くは、その構造の違いに起因する。
- 値で渡されるのか
- 参照でつながっているのか
- それがどのスコープで共有されるのか
この3点を見極めることが、副作用のないロジックと保守性の高い設計を作る鍵になる。
コードを制御するとは、状態と記憶を理解することだ。
JavaScriptの型は、ただの仕様ではなく、「どこで何が変わるか」を読み解くためのレンズである。