ウェブエンジニア問題集
第2章

JavaScriptのデータ型と型変換 — プリミティブ・typeof・オブジェクトの違い

16
この章の目次開く

JavaScriptは「動的型付け」の言語です。変数に型が固定されるのではなく、値そのものが stringnumberboolean などの型を持ちます。

前章の変数宣言とスコープでは、値に名前を付ける const / let / var を扱いました。この章では、その「値」にはどんな種類があり、比較や計算のときにどう変換されるのかを整理します。

学習者学習者

typeof null"object" だったり、"" == falsetrue だったり、JavaScriptの型まわりは直感と違うことが多くて怖いです…。

先に結論を出しておくと、JavaScriptの型でまず覚えるべき軸は「プリミティブ値」と「オブジェクト」の違い、そして暗黙的な型変換を避けるために === を使うことです。


JavaScriptのデータ型の全体像

JavaScriptの値は、大きく分けるとプリミティブ値オブジェクトに分かれます。

プリミティブ値は、言語の基本になる単純な値です。オブジェクトは、プロパティやメソッドを持てる複合的な値です。

const name = 'Taro'; // string(プリミティブ)
const age = 25; // number(プリミティブ)
const user = { name: 'Taro', age: 25 }; // object(オブジェクト)
const items = ['book', 'pen']; // object(配列もオブジェクト)
js

typeof で調べると、配列も関数も広い意味ではオブジェクトとして扱われることが分かります。

typeof 'Taro'; // "string"
typeof 25; // "number"
typeof { name: 'Taro' }; // "object"
typeof ['book', 'pen']; // "object"
typeof function () {}; // "function"
js
データの種類を整理しているイメージ

7つのプリミティブ型

プリミティブ型は、オブジェクトではない基本的な値です。JavaScriptには次の7種類があります。

主な用途
string'hello', "JavaScript"文字列、入力値、メッセージ
number42, 3.14, NaN, Infinity数値計算、小数、特殊な数値
bigint9007199254740993nnumber で安全に扱えない大きな整数
booleantrue, false条件分岐、フラグ
undefinedundefined値がまだ入っていない状態
nullnull意図的に「値がない」と示す
symbolSymbol('id')一意なプロパティキー

string

string は文字列です。ユーザー名、メールアドレス、URL、画面に表示するメッセージなど、テキストを扱います。

const message = 'こんにちは';
const url = `https://example.com/users/${userId}`;
js

文字列操作の詳しいメソッドは、文字列操作と正規表現で扱います。

number

number は整数も小数も扱う数値型です。JavaScriptには、一般的な意味での intfloat という別々の型はありません。

const count = 10;
const rate = 0.08;
const total = 1200 * (1 + rate);
js

NaNInfinitynumber 型です。この章の後半で詳しく扱います。

bigint

bigint は、非常に大きな整数を安全に扱うための型です。末尾に n を付けて書きます。

const id = 9007199254740993n;
typeof id; // "bigint"
js

bigintnumber は、そのまま混ぜて計算できません。

1n + 1; // TypeError
1n + BigInt(1); // 2n
js

boolean

booleantrue / false の2値です。条件分岐やフラグに使います。

const isLoggedIn = true;
 
if (isLoggedIn) {
  console.log('マイページを表示');
}
js

undefined

undefined は「値がまだ定義されていない」ことを表します。

let name;
console.log(name); // undefined
 
const user = {};
console.log(user.email); // undefined
js

何も return しない関数の戻り値も undefined です。

function logMessage() {
  console.log('hello');
}
 
const result = logMessage(); // undefined
js

null

null は、開発者が意図的に「値がない」と入れる値です。

let selectedUser = null;
 
// ユーザーを選択したらオブジェクトが入る
selectedUser = { id: 1, name: 'Taro' };
js

undefined は「まだ入っていない」、null は「空であることを明示した」と考えると使い分けやすいです。

symbol

symbol は一意な値を作るための型です。同じ説明文字列を渡しても、別々の値になります。

const a = Symbol('id');
const b = Symbol('id');
 
console.log(a === b); // false
js

実務では、ライブラリ内部や特殊なプロパティキーで使われることが多く、初学者が頻繁に書く型ではありません。


typeof演算子

typeof は、値の型を文字列で返す演算子です。デバッグや条件分岐でよく使います。

構文: typeof value

要素説明
value型を調べたい値、変数、式

戻り値: 型を表す文字列

typeof 'hello'; // "string"
typeof 123; // "number"
typeof 123n; // "bigint"
typeof true; // "boolean"
typeof undefined; // "undefined"
typeof Symbol('id'); // "symbol"
typeof {}; // "object"
typeof []; // "object"
typeof function () {}; // "function"
js

typeofの早見表

typeof の結果
'hello'"string"
123"number"
123n"bigint"
true"boolean"
undefined"undefined"
Symbol('id')"symbol"
{}"object"
[]"object"
null"object"
() => {}"function"

最重要の落とし穴は null です。

typeof null; // "object"
js

これは歴史的な仕様上の挙動です。null を判定したいときは、typeof ではなく value === null を使います。

const value = null;
 
value === null; // true
typeof value === 'object'; // true だが、null判定としては不適切
js

配列かどうかを調べたい場合も typeof では不十分です。

const list = [1, 2, 3];
 
typeof list; // "object"
Array.isArray(list); // true
js

配列の扱いは 配列メソッド早見表 から目的別に確認できます。


プリミティブとオブジェクトの違い

プリミティブ値とオブジェクトの違いは、代入や比較の挙動に強く現れます。

プリミティブ値は値そのものを扱う

プリミティブ値を別の変数に代入すると、値がコピーされたように振る舞います。

let a = 1;
let b = a;
 
b = 2;
 
console.log(a); // 1
console.log(b); // 2
js

文字列も同じです。

let x = 'hello';
let y = x;
 
y = 'bye';
 
console.log(x); // "hello"
console.log(y); // "bye"
js

オブジェクトは参照先を共有する

オブジェクトを別の変数に代入すると、同じオブジェクトを指す参照がコピーされます。

const userA = { name: 'Taro' };
const userB = userA;
 
userB.name = 'Hanako';
 
console.log(userA.name); // "Hanako"
console.log(userB.name); // "Hanako"
js

userAuserB は別々の箱に見えますが、中身は同じオブジェクトを指しています。

学習者学習者

const なのにオブジェクトの中身を変えられるのは、前章でも出てきた話ですね。

先生先生

そうです。const が固定するのは「変数がどの値を指すか」です。オブジェクトのプロパティ変更までは禁止しません。

オブジェクト同士の比較は中身ではなく参照を見る

オブジェクトは、中身が同じでも別々に作られたものなら === で一致しません。

const a = { id: 1 };
const b = { id: 1 };
const c = a;
 
console.log(a === b); // false
console.log(a === c); // true
js

配列も同じです。

[1, 2, 3] === [1, 2, 3]; // false
js
JavaScriptは「オブジェクトの中身が同じか」ではなく、「同じオブジェクトを指しているか」で === を判定します。

暗黙的型変換

JavaScriptでは、演算や比較のときに自動で型が変換されることがあります。これを暗黙的型変換、または型強制と呼びます。

'1' + 2; // "12"
'5' - 2; // 3
true + 1; // 2
js

便利に見える一方で、予想外の結果を生みやすい場所でもあります。

文字列結合の +

+ は、数値の足し算にも文字列結合にも使われます。片方が文字列だと、もう片方も文字列に変換されやすくなります。

1 + 2; // 3
'1' + 2; // "12"
2 + '1'; // "21"
js

フォーム入力やURLクエリから来た値は、見た目が数字でも文字列であることが多いです。

const price = '1000';
const tax = 100;
 
price + tax; // "1000100"
Number(price) + tax; // 1100
js

=====

== は比較前に型変換します。=== は型変換せず、型と値が同じときだけ true になります。

1 == '1'; // true
1 === '1'; // false
 
'' == false; // true
'' === false; // false
 
null == undefined; // true
null === undefined; // false
js

実務では、基本的に === を使います。== の詳しい比較ルールは次章の演算子と式でさらに掘り下げます。

falsy値一覧

条件式では、真偽値でない値も true / false 相当として扱われます。

false 相当として扱われる値を falsy と呼びます。

falsy値説明
falsebooleanのfalse
0数値の0
-0負の0
0nbigintの0
''空文字列
null値がないことを明示
undefined値が定義されていない
NaN数値として成立しない結果

それ以外の値は基本的に truthy です。空配列 [] や空オブジェクト {} も truthy です。

Boolean(''); // false
Boolean('hello'); // true
Boolean([]); // true
Boolean({}); // true
js
truthyとfalsyの判定で迷っているイメージ

明示的型変換

型変換が必要なときは、JavaScriptに任せるのではなく、自分で明示します。

Number

Number() は値を数値に変換します。

構文: Number(value)

引数説明
value数値へ変換したい値

戻り値: 変換後の number。変換できない場合は NaN

Number('123'); // 123
Number('3.14'); // 3.14
Number(''); // 0
Number('hello'); // NaN
Number(true); // 1
Number(false); // 0
Number(null); // 0
Number(undefined); // NaN
js

フォーム入力は文字列なので、計算する前に Number() で変換することが多いです。

const input = '1200';
const price = Number(input);
 
if (Number.isNaN(price)) {
  console.log('数値を入力してください');
}
js

String

String() は値を文字列に変換します。

構文: String(value)

引数説明
value文字列へ変換したい値

戻り値: 変換後の string

String(123); // "123"
String(true); // "true"
String(false); // "false"
String(null); // "null"
String(undefined); // "undefined"
js

画面表示やログ出力のために、値を明示的に文字列化したいときに使います。

Boolean

Boolean() は値を真偽値に変換します。

構文: Boolean(value)

引数説明
value真偽値へ変換したい値

戻り値: 変換後の boolean

Boolean('hello'); // true
Boolean(''); // false
Boolean(1); // true
Boolean(0); // false
Boolean(null); // false
Boolean([]); // true
js

Boolean() は、値が truthy か falsy かを確認したいときに便利です。

parseInt

parseInt() は文字列を整数として解析します。

構文: parseInt(string, radix?)

引数説明
string解析したい文字列。文字列でない値は文字列に変換される
radix(省略可)何進数として読むか。通常は 10 を指定する

戻り値: 解析できた整数。先頭から数値として読めない場合は NaN

parseInt('123', 10); // 123
parseInt('123px', 10); // 123
parseInt('3.14', 10); // 3
parseInt('hello', 10); // NaN
js

CSSの px 値のように、先頭の整数部分だけ取りたいときに使います。

parseFloat

parseFloat() は文字列を小数として解析します。

構文: parseFloat(string)

引数説明
string解析したい文字列。文字列でない値は文字列に変換される

戻り値: 解析できた数値。先頭から数値として読めない場合は NaN

parseFloat('3.14'); // 3.14
parseFloat('3.14px'); // 3.14
parseFloat('hello'); // NaN
js

Number() は文字列全体が数値として解釈できる必要があります。一方、parseInt() / parseFloat() は先頭から読めるところまでを数値にします。

Number('123px'); // NaN
parseInt('123px', 10); // 123
parseFloat('12.5px'); // 12.5
js

NaNとInfinity

NaNInfinity は、どちらも number 型の特殊な値です。

typeof NaN; // "number"
typeof Infinity; // "number"
js

NaN

NaNNot a Number の略で、数値として成立しない計算結果を表します。

Number('hello'); // NaN
0 / 0; // NaN
Math.sqrt(-1); // NaN
js

NaN 最大の特徴は、自分自身とも等しくないことです。

NaN === NaN; // false
js

NaN かどうかを調べるときは Number.isNaN() を使います。

構文: Number.isNaN(value)

引数説明
valueNaN かどうか調べたい値

戻り値: value が数値の NaN なら true、それ以外は false

Number.isNaN(NaN); // true
Number.isNaN(Number('hello')); // true
Number.isNaN('hello'); // false
js

Infinity

Infinity は無限大を表す数値です。0で割ったり、表現できる範囲を超える計算をしたりすると登場します。

1 / 0; // Infinity
-1 / 0; // -Infinity
Number.MAX_VALUE * 2; // Infinity
js

有限の数値かどうかは Number.isFinite() で確認できます。

構文: Number.isFinite(value)

引数説明
value有限の数値か調べたい値

戻り値: value が有限の number なら trueNaN / Infinity / 数値以外なら false

Number.isFinite(100); // true
Number.isFinite(Infinity); // false
Number.isFinite(NaN); // false
Number.isFinite('100'); // false
js

数値の扱い、丸め、浮動小数点の誤差は 数値の扱いと計算 で詳しく扱います。


よくあるハマりどころ

typeof null"object"

typeof null; // "object"
js

null 判定には value === null を使います。

"" == falsetrue

'' == false; // true
js

== が型変換を行い、どちらも 0 相当として比較されるためです。実務では === を使い、必要なら自分で Boolean(value)Number(value) を使って変換します。

null == undefinedtrue

null == undefined; // true
null === undefined; // false
js

== には、nullundefined を等しいとみなす特別ルールがあります。両方をまとめて「値がない」と扱いたい場合に value == null を使うコードもありますが、初学者のうちは明示的に書く方が安全です。

value === null || value === undefined;
js

空配列と空オブジェクトはtruthy

Boolean([]); // true
Boolean({}); // true
js

「中身が空だからfalse」とはなりません。配列が空かどうかは array.length === 0 で判断します。

const items = [];
 
if (items.length === 0) {
  console.log('空です');
}
js

Number('')0

Number(''); // 0
js

フォーム入力で空文字を数値変換すると 0 になります。「未入力」と「0」を区別したい場合は、変換前に空文字を確認します。

const input = '';
 
if (input === '') {
  console.log('未入力です');
} else {
  const value = Number(input);
}
js

オブジェクトを浅くコピーしたつもりでネストを共有する

スプレッド構文でオブジェクトをコピーしても、ネストしたオブジェクトまでは深くコピーされません。

const original = {
  profile: { name: 'Taro' },
};
 
const copied = { ...original };
copied.profile.name = 'Hanako';
 
console.log(original.profile.name); // "Hanako"
js

深いコピーやJSON変換の注意点は、JSONの基礎と実践で扱います。


ちゃんと使うためのポイント

  • JavaScriptの値は「プリミティブ値」と「オブジェクト」に分けて考える
  • プリミティブ型は string / number / bigint / boolean / undefined / null / symbol の7種類
  • typeof null"object" になる。null 判定は value === null
  • オブジェクトや配列は === で中身比較されない。同じ参照かどうかを見ている
  • == は暗黙的型変換を行う。実務では基本的に === を使う
  • 数値変換は Number()、単位付き文字列の先頭だけ取りたいときは parseInt() / parseFloat()
  • NaN 判定は Number.isNaN()、有限数判定は Number.isFinite()

参考リンク

次の章では、=====、短絡評価、オプショナルチェーンなど、型変換とも関係が深い**演算子と式**を扱います。

JavaScriptクイズに挑戦するこの章で学んだJavaScriptの知識を、4択クイズでアウトプットして定着させよう