プリミティブ型 と オブジェクト型
- プリミティブ型(不変 / 値渡し):
Number,BigInt,String,Boolean,undefined,null,Symbol
→ 値そのものを持ち、変数に代入すると値がコピーされる。メソッド呼び出し時に一時的にラッパーオブジェクトが使われる(ボクシング)。 - オブジェクト型(参照渡し):
Object,Array,Function,Date,RegExp,Map,Set, …
→ 変数はオブジェクトへの参照(ポインタ)を持つ。代入は参照のコピー。ミューテートすると参照先が変化する。
プリミティブ型を個別に解説(例+注意点)
1) Number(数値)
- JS の
Numberは IEEE-754 の 64-bit 浮動小数(整数も浮動小数として扱う)。 - 特殊値:
NaN(Not a Number),Infinity,-Infinity,+0と-0が存在。
console.log(typeof 3.14); // "number"
console.log(0.1 + 0.2); // 0.30000000000000004 (浮動小数の丸め誤差)
console.log(Number.isNaN(NaN)); // true
console.log(isNaN("foo")); // true (global isNaN は coerces する)
console.log(Number.isNaN("foo"));// false( coercion しない)
console.log(typeof NaN); // "number"注意点/ベストプラクティス:
- 浮動小数の比較は直接等価を使うと失敗しがち → 小さな誤差を許容する比較(
Math.abs(a-b) < ε)を使う。 Number.isNaNを使う(isNaNは型変換後に判定してしまう)。
2) BigInt(大きな整数)
- 整数を無限に近い大きさまで表現できる。リテラルは末尾に
nを付ける:123n。 - Number と混在して演算できない(TypeError)。必要なら明示変換する。
console.log(typeof 123n); // "bigint"
console.log(10n + 20n); // 30n
// 10n + 2 // TypeError: cannot mix BigInt and other types注意:
- 小数(非整数)とは扱いが異なる。BigInt は整数演算向け。
Math.*は BigInt を受け取らない。
3) String(文字列)
- イミュータブル(変更不可)。
''/""/ ““(テンプレート)で表現。 - テンプレート文字列(バッククォート)で式展開や改行が楽。
const s = "Hello";
console.log(typeof s); // "string"
console.log(`値は ${1+2}`); // "値は 3"
console.log("あいう".length); // .length は文字単位(サロゲートペアは注意)
console.log("a".toUpperCase()); // "A" (プリミティブにメソッドが呼べるのは一時ボクシング)注意:
- 文字列は不変。変更したければ新しい文字列を作る。
- Unicode の取り扱い(サロゲートペア, 正規化
normalize())に注意。
4) Boolean(真偽)
- 値は
trueまたはfalse。 - JS には truthy / falsy の概念がある(条件式で真偽変換される)。
Falsy 値(false と判定されるもの):false, 0, -0, 0n, ""(空文字), null, undefined, NaN
Boolean("") // false
Boolean("0") // true (非空文字列は truthy)注意:
new Boolean(false)はオブジェクトで「truthy」になる → wrapper オブジェクトは使わない。
5) undefined と null(似て非なるもの)
undefined:値が未定義(変数宣言のみで代入していない、関数が値を返さない等)。null:意図的に「値がない」ことを示すために代入する値。typeof null === "object"は歴史的な仕様(バグ)に由来するが現在も残る。
let a;
console.log(a); // undefined
console.log(typeof null); // "object" (注意)
console.log(null == undefined); // true
console.log(null === undefined); // false注意:
Symbolプロパティはfor...inで列挙されないがObject.getOwnPropertySymbols(obj)で取得できる。- JSON にシンボルはシリアライズされない。
オブジェクト型(参照型)のポイント
- 値は参照(ポインタ)として扱われる。コピーは参照のコピー。
- ミューテーション(
obj.a = 1,arr.push())は参照先を変える。プリミティブとは違う。 - よく使うオブジェクト型:
Object,Array,Function,Date,RegExp,Map,Set,WeakMap,WeakSet
const o = { x: 1 };
const p = o;
p.x = 2;
console.log(o.x); // 2 (同じ参照を指す)配列メソッドの「破壊的/非破壊的」:
- 破壊的(ミューテート):
push,pop,shift,unshift,splice,sort,reverse - 非破壊(新配列を返す):
slice,concat,map,filter,reduce
浅いコピー vs 深いコピー
- 浅いコピー:
Object.assign({}, obj)/{...obj}→ ネストしたオブジェクトは参照のまま - 深いコピー:
structuredClone(obj)(環境次第)、JSON.parse(JSON.stringify(obj))(関数・undefined・Symbol を失う)
型判定・チェックの実用テクニック
typeof:プリミティブの多くに使えるが null と配列は注意。typeof null === "object"(歴史的バグ)typeof [] === "object"typeof function(){} === "function"(関数は special-case)
- 配列の判定:
Array.isArray(value)(推奨) - より確実な判定(内部 [[Class]] を見る):
Object.prototype.toString.call(value)→"[object Array]","[object Date]","[object Null]"など instanceof:プロトタイプチェーンに基づく判定(クロスフレームに注意)
console.log(typeof []); // "object"
console.log(Array.isArray([])); // true
console.log(Object.prototype.toString.call([])); // "[object Array]"等価比較 (== と ===) と Object.is
===(厳密等価):型も値も一致(推奨)==(抽象等価):型変換を伴う比較(予測しにくい挙動がある)Object.is:NaNと-0を区別したいときに便利Object.is(NaN, NaN) === trueObject.is(+0, -0) === false
例:
0 == "0" // true
0 === "0" // false
null == undefined // true
null === undefined // false
NaN === NaN // false
Object.is(NaN, NaN) // true
Object.is(0, -0) // false注意:
- 基本的に
===を使う。==は型変換のルールを正確に理解している場合のみ。
型変換(Coercion) — 暗黙の変換に要注意
+演算子は片方が文字列だと連結になる:1 + "2" === "12"- 数値化のショートカット:
+value(一項プラス) - 真偽化:
Boolean(value)または!!value - 文字列化:
String(value)またはvalue + ''
console.log(1 + "2"); // "12"
console.log(+ "123"); // 123
console.log(!![]); // true (空配列は truthy)注意:
- 型変換のルールは複雑 → 明示的な変換(
Number(),String(),Boolean())が安全。
イミュータブル vs ミュータブル、コピーの挙動まとめ
- プリミティブはイミュータブル(例:文字列に変更はできない)。
- オブジェクトはミュータブル(メソッドで変更できる)。
- 代入の振る舞い:
let a = 1;
let b = a;
b = 2;
console.log(a); // 1 (プリミティブは値がコピーされる)
let o1 = {n:1};
let o2 = o1;
o2.n = 9;
console.log(o1.n); // 9 (参照のコピー)よくある落とし穴(まとめ)
typeof null === "object"(混乱のもと) → 判定には=== nullやObject.prototype.toStringを使う。NaNは自分自身と等しくない(NaN !== NaN) →Number.isNaNまたはObject.isを使う。- 浮動小数の丸め誤差(
0.1 + 0.2 === 0.3は false) ==の予期せぬ挙動("" == 0→ true など) →===を使うnew Number,new String,new Booleanを使わない(ラッパーオブジェクトは truthy)BigIntとNumberを混ぜない- JSON シリアライズでは
undefined,function,Symbolは通常失われる- 配列中の
undefinedはnullに変換されることがある(JSON.stringify([undefined])→[null])
- 配列中の
- 配列判定に
typeofを使うと失敗 →Array.isArrayを使う
実践的なチェックリスト(短い手引き)
- 型を厳密に比較したい →
=== - 配列の判定 →
Array.isArray(x) nullとundefinedを判定 →x == nullで両方チェック(==だとnull/undefined両方 true)- NaN 判定 →
Number.isNaN(x)またはObject.is(x, NaN) - オブジェクトの浅いコピー →
{...obj}/Object.assign({}, obj) - オブジェクトの深いコピー →
structuredClone(obj)(環境があれば) or ライブラリ
参考となるコードスニペット(まとめ)
// typeof の結果
console.log(typeof 42); // "number"
console.log(typeof 42n); // "bigint"
console.log(typeof "hello"); // "string"
console.log(typeof true); // "boolean"
console.log(typeof undefined); // "undefined"
console.log(typeof null); // "object" // 注意
console.log(typeof Symbol()); // "symbol"
console.log(typeof {}); // "object"
console.log(typeof []); // "object"
console.log(Array.isArray([])); // true
console.log(typeof (x=>x)); // "function"
// 等価比較の例
console.log(0 == "0"); // true
console.log(0 === "0"); // false
console.log(null == undefined); // true
console.log(Object.is(NaN, NaN)); // trueまとめ(短く)
- JavaScript の型は プリミティブ(不変) と オブジェクト(参照) に分かれる。
typeof・===・Array.isArray・Object.is等を組み合わせて安全に判定する。- 暗黙の型変換(coercion)と浮動小数の丸め誤差に注意。
BigIntやSymbolといったモダン機能も覚えておくと便利。
