Menu
日本語

JavaScriptのプリミティブ型7種類を完全理解

JavaScriptのプリミティブ型は全部で7つ。string・number・bigint・boolean・null・undefined・symbolの特徴と、オブジェクト型との違いを整理します。

7つのプリミティブとそれ以外

JavaScript の値は大きく2つの世界に分かれます。片方は7種類の プリミティブ型 で、シンプルかつイミュータブルな値たち。もう片方は オブジェクト で、複合的・ミュータブル・呼び出し可能なものはすべてこちらに入ります。値レベルで見た JavaScript の型システムは、実はこれだけです。

プリミティブ型は次の7つです。

index.js
Output
Click Run to see the output here.

このリストに出てこないもの(配列、関数、Date、正規表現、素の {} など)はすべてオブジェクトです。実行時の型を調べるには typeof を使いますが、最後の行で有名な「あのバグ」に気づくはずです。typeof null は 1995 年以来ずっと 'object' を返し続けていて、これはもう直されることはありません。既存のコードが大量にこの挙動に依存しているからです。

プリミティブは「入れ物」ではなく「値そのもの」

一番しっくりくる捉え方はこれです。プリミティブは値「そのもの」だということ。数値の 3 は「3 という値を入れた箱」ではなく、ただの 3 です。3 を持つ 2 つの変数は、どこかにある共有の何かを指す 2 つのコピーではなく、同じ値そのものを持っているだけです。

index.js
Output
Click Run to see the output here.

プリミティブ型は値で比較され、オブジェクトは参照で比較されます。この違いこそが、後々「なんでこれが false になるの?」と悩む原因になります。特に配列やオブジェクトを === で比較するときに顕著です。

JavaScript のプリミティブ型はイミュータブル

プリミティブ型は変更できません。一見変更しているように見える操作も、実際には新しい値を生成しているだけです。

index.js
Output
Click Run to see the output here.

最初の呼び出しでは新しい文字列が作られていますが、戻り値を受け取っていないので捨てられてしまいます。一方、2 つ目は name に再代入しています。どちらの場合も、元の "ada" そのものが書き換わったわけではありません。そもそも書き換えることは不可能です。数値も同じで、x + 1 は新しい数値を作るだけで、x を変更しているわけではありません。

これこそが、文字列や数値に const を使っても本当に安全だと言える理由です。値自体が変わることはありませんし、const を使えば変数の再代入もブロックされます。

number と BigInt、なぜ 2 種類あるのか

JavaScript の number は 64 ビット浮動小数点数です。おかげで演算は高速ですが、上限もあります。整数として正確に扱えるのは Number.MAX_SAFE_INTEGER(2^53 - 1)までです。

index.js
Output
Click Run to see the output here.

この境界を超えると、整数同士がぶつかり始めます。どんなに大きくなっても厳密な値を保ちたいときに使うのが bigint です。書き方は末尾に n を付けるだけです。

index.js
Output
Click Run to see the output here.

bigintnumber を混ぜて演算することはできません。混ぜられたら、わざわざ精度を拡張した意味がなくなってしまいますからね。bigint の出番は、データベースの ID、ナノ秒単位のタイムスタンプ、暗号処理などを扱うときです。普通の計算は number のままで十分です。

文字列もプリミティブ型

JavaScript の文字列はプリミティブ型であって、オブジェクトではありません。.length.slice.toUpperCase といったメソッドが使えるので紛らわしいのですが、れっきとしたプリミティブです:

index.js
Output
Click Run to see the output here.

内部的には、文字列のメソッドを呼び出すと、JavaScript はその場で String オブジェクトにラップしてメソッド呼び出しを成立させ、終わったらラッパーを捨てています。このラッパーのことを意識する必要はありません。ただ、文字列は豊富なメソッドを持ってはいるものの、値として振る舞う(イミュータブルで、値で比較される)という点だけ覚えておけば十分です。

シングルクォート、ダブルクォート、バッククォートはどれも同じ型を作ります。ただしバッククォートだけは、文字列への値の埋め込み(テンプレートリテラル)と複数行の記述が可能で、これについては次のドキュメントで扱います。

null と undefined の違い

「値がない」ことを表すプリミティブが 2 つあり、両者は使い分けが必要です。

undefined は、そもそも値が一度も代入されていないときに得られる値です。宣言だけして初期化していない変数、渡されなかった関数の引数、存在しないプロパティなどがこれにあたります。

index.js
Output
Click Run to see the output here.

null は「意図的に空っぽにしておくよ」と伝えたいときに、自分で明示的に書く値です。

index.js
Output
Click Run to see the output here.

だいたいの使い分けとしては、undefined は言語側が「ここには何もないよ」と言うときのもの、null はプログラマー自身が「何もない」と明示するときのもの、と覚えておけばOKです。どちらも falsy 扱いで、普通の値との等価比較は失敗します。詳しい話はそれぞれ別ページで改めて取り上げます。

Symbol 型:作った瞬間から必ずユニーク

プリミティブの中でいちばん出番が少ないのが symbol 型です。Symbol は作るたびに必ずユニークな値になります。たとえ同じ説明文字列から作ったとしても、別物として扱われます。

index.js
Output
Click Run to see the output here.

シンボルはオブジェクトのキーとして便利で、既存のキーとぶつかる心配がありません。ライブラリがあなたのオブジェクトにメタデータを付けたいとき、シンボルを使えば他のコードに上書きされる心配がないというわけです。のちほどイテレータや Symbol.iterator のようなwell-knownシンボルを扱う場面で、また登場します。

実行時に型を調べる方法

たいていのケースは typeof で事足ります。ただし、いくつかクセがあるので押さえておきましょう。

index.js
Output
Click Run to see the output here.

null の判定は value === null のように直接比較します。配列なら Array.isArray(value) を使いましょう。「プリミティブ型かどうか」を一発で判定する組み込み関数はありませんが、次のようなイディオムがよく使われます。

index.js
Output
Click Run to see the output here.

プリミティブとオブジェクトの違い:代入でハマるポイント

先へ進む前に、もう一つ見ておきたいポイントがあります。プリミティブは「値そのもの」、オブジェクトは「参照」なので、代入したときの挙動がまったく違ってくるんです。

index.js
Output
Click Run to see the output here.

プリミティブ型では b = a は値のコピーになります。一方、オブジェクトの場合 y = x は参照のコピーで、両方の名前が同じ実体を指します。片方を通して変更すると、もう片方にも影響します。JavaScript で「え、なんで勝手に変わってるの?」という不具合を生む最大の原因がこれです。

このページのまとめ

  • プリミティブは7種類: stringnumberbigintbooleannullundefinedsymbol。これ以外は全部オブジェクトです。
  • プリミティブはイミュータブルで値で比較され、オブジェクトはミュータブルで参照で比較されます。
  • typeof は実行時に型を返してくれますが、覚えておきたいクセが2つあります: typeof null === "object"typeof function(){} // "function" です。
  • number は64ビット浮動小数点数で、整数として安全に扱える上限があります。その上限を超える正確な整数が必要なときのために bigint が用意されています。

次回: 文字列とテンプレートリテラル

文字列は一番よく触るプリミティブです。そして、テンプレートリテラル(バッククォートで囲む文字列)を使えば、変数の埋め込みや複数行のテキスト、タグ付きテンプレートまで楽に書けます。次のページで扱います。

よくある質問

JavaScriptのプリミティブ型はいくつある?

全部で7つです。stringnumberbigintbooleannullundefinedsymbol の7種類。これ以外の配列、関数、Date、オブジェクトリテラルなどはすべてオブジェクト型になります。実行時に型を調べたいときは typeof を使いますが、歴史的な経緯で typeof null だけは 'object' を返すので要注意です。

プリミティブとオブジェクトは何が違うの?

プリミティブは値そのもので比較される、イミュータブルな値です。33 はどちらも同じ 3。一方、オブジェクトは参照で比較されるミュータブルな存在なので、見た目が同じ {} でも別物扱いになります。変数に代入するときも、プリミティブなら値がコピーされますが、オブジェクトの場合は同じ実体を指す参照がコピーされる、という違いがあります。

JavaScriptのプリミティブは本当に変更できないの?

はい、プリミティブはイミュータブルです。'hello'.toUpperCase() は新しい文字列を返すだけで、元の 'hello' は一切変わりません。x = x + 1 のような再代入も、変数が指す先を別のプリミティブに差し替えているだけで、元の値をいじっているわけではないんです。だからこそ const name = 'Ada' と宣言しても、name を元にした新しい文字列を作ることは問題なくできます。

なぜ typeof null は 'object' を返すの?

1995年の初期実装のバグが原因です。ただ、すでに多くのコードがこの挙動に依存していたため、今も修正されずに残っています。null を判定したいときは value === null のように === で比較してください。undefined なら value === undefinedtypeof value === 'undefined' を使うのが定番です。

Coddyでコードを学ぼう

始める