JavaScript | データ型

javascrpit JavaScript
スポンサーリンク

プリミティブ型 と オブジェクト型

  • プリミティブ型(不変 / 値渡し)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.isNaN-0 を区別したいときに便利
    • Object.is(NaN, NaN) === true
    • Object.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"(混乱のもと) → 判定には === nullObject.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)
  • BigIntNumber を混ぜない
  • JSON シリアライズでは undefined, function, Symbol は通常失われる
    • 配列中の undefinednull に変換されることがある(JSON.stringify([undefined])[null]
  • 配列判定に typeof を使うと失敗 → Array.isArray を使う

実践的なチェックリスト(短い手引き)

  • 型を厳密に比較したい → ===
  • 配列の判定 → Array.isArray(x)
  • nullundefined を判定 → 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.isArrayObject.is 等を組み合わせて安全に判定する。
  • 暗黙の型変換(coercion)と浮動小数の丸め誤差に注意。BigIntSymbol といったモダン機能も覚えておくと便利。
タイトルとURLをコピーしました