#概要
この記事では、JavaScriptのデータ型について理解していきます。JavaScriptにおいて、型の理解は非常に大事なので、しっかり理解しておきたい部分です。
では、いきましょう!
#データ型とは?
そもそもデータ型とはなんなのでしょうか?調べてみると次のような記述がありました。
プログラミングにおけるデータ型あるいは単に型は、値の種類を示し分類分けするラベルである。データタイプともいう。
引用:Wikipedia
つまり、簡単にいうと値のカテゴリー分けのことになります。例えば、「10」だったら、数値型のように、各プログラミング言語で決まっています。
もちろん、JavaScriptにも型は存在します。
#JavaScriptは型を持っている
「JavaScriptにも型は存在する」と聞いて、「JavaScriptって型がない言語なんじゃないの?」って思っている方がいるかもしれません。これは、間違いです。
「型がある・ない」という表現は、誤解を招きやすいためあまり使わないほうが良いです。
JavaScriptには型が存在します。ここをまず押さえておいてください。
##JavaScriptは動的型付け言語である
なぜ、JavaScriptが型がない言語であると誤解されやすいのでしょうか?
それは、JavaScriptが「動的型付け言語」であるためです。
「動的型付け言語」とは、変数や関数の引数・戻り値と言った値の型が、プログラムの実行時の値によって動的に変化する言語です。
JavaScriptで変数を宣言する際は、変数名だけを決めますよね。
動的型付け言語と対をなす性質を持った言語を「静的型付け言語」と呼びます。
「静的型付け言語」では、変数や関数の引数・戻り値と言った値の型をあらかじめ決めておかなければいけません。
例えば、TypeScriptでは、変数を宣言する際に変数名だけではなく、その変数にはどのような型の値が入るのかを指定しておきます。
//JavaScriptによる変数の宣言
let a = 10;
a = 'Messi';
//TypeScriptによる変数の宣言
let b: number = 10;
b = 'Messi';
//コンパイルエラーになる
JavaScriptの例では、let
で変数a
を定義し、10
を代入しています。その後、a
をMessi
に書き換えてもエラーにはなりません。
一方、TypeScriptの例では、let
で変数b
を定義する際に、b
の型を数値型に指定しています。そのため、文字列型である'Messi'に値を更新すると、エラーが表示されます。
JavaScriptは「動的型付け言語」であり、「動的型付け言語」では値の型をあらかじめ決めないという性質から、JavaScriptには型がないという誤解をされやすいのです。
#プリミティブ型とオブジェクト型
ここまでで、JavaScriptにも型があるということを理解していただけたと思います。
では、JavaScriptのデータ型はどのように分類されているのでしょうか?
結論から言いますと、JavaScriptのデータ型は「プリミティブ型」と「オブジェクト型」の2種類に大別されます。
それぞれについて説明します。
簡単にいうと、「プリミティブ型」とは、オブジェクトではないもの、「オブジェクト型」とは、プリミティブ値でないもののことです。
すみません、ちゃんと説明します。
「プリミティブ型(基本型)」は、真偽値や数値などの基本的な型のことです。プリミティブ型の値は、一度作成したらその値を変更することができません。(この特性をイミュータブルと呼ぶ)
一方、「オブジェクト型(複合型)」は、複数のプリミティブ型の値またはオブジェクトからなる集合体です。オブジェクトは、一度作成した後も、その値自体を変更することができます。(この特性をミュータブルと呼ぶ)
JavaScriptのデータ型は、このような定義で分類されています。
##プリミティブ型とリテラル
プリミティブ型は、さらに以下の7種類に分けられます。
- Boolean型
- Number型
- BigInt型
- String型
- Symbol型
- Null型
- Undefined型
typeof
演算子を使うことでデータ型を確認することができます。
console.log(typeof "Messi");
console.log(typeof true);
"string"
"boolean"
また、それぞれの型を定義するには「リテラル(Literal)」を使います。「リテラル」とは、プログラム上で数値や文字列など、データ型の値を直接記述できるように構文として定義されたものです。
Symbol型とUndefined型以外のプリミティブ型には、リテラルが用意されています。
では、それぞれの型とそのリテラルについて説明していきます。
###Boolean型
Boolean型とは、ture
とfalse
の2つの真偽値を扱うデータ型です。
Boolean型には、true
とfalse
の真偽値リテラルがあります。
###Number型
Number型とは、数値を扱うためのデータ型です。JavaScriptのNumber型では、整数も少数も同じNumber型として扱われます。扱うことのできる最大値は253–1です。
Number型には、10
や-7
などの数値リテラルと1.4
や5.4e3
などの浮動小数点リテラルがあります。
###BigInt型
BigInt型とは、Number型で取り扱うことのできない大きな数値を扱うためのデータ型です。ES2020で追加されました。Number型との互換性はなく、相互に代入や計算・等値比較などは行うことができません。
200n
などのように数字の後ろにn
をつけて表現する数値リテラルがあります。
###String型
テキストを表す連続した文字である文字列を扱うためのデータ型です。
"Messi"
のように、'
もしくは"
で囲んだ範囲が文字列リテラルとなります。`
を用いることで、改行を含む複数行テキストや${}
による式の展開が可能なテンプレートリテラルになります。
###Symbol型
Symbol型とは、「シンボル値」という固有の識別子を表現する値で、任意の不変な値のデータ型です。ES2015で追加されました。
Symbol()
関数を呼び出すことで同的に生成されます。同じシンボル値を後から生成することはできません。オブジェクトのプロパティキーとして使用することができます。
Symbol型は、リテラル表現を持ちません。
###Null型
Null型とは、値が存在しないことを意味するデータ型です。
nullリテラルであるnull
は、プリミティブ値null
を返します。
ただしここで注意点があります。typeof
演算子でnull
のデータ型を調べるとobject
となります。
console.log(typeof null);
"object"
これは、残念なことにJavaScriptの歴史的経緯からなるバグです。
###Undefined型
Undefined型とは、値が未定義であることを意味するデータ型です。プリミティブ値undefined
は、宣言のみが行われた変数やオブジェクト内の存在しないプロパティへのアクセスに割り当てられます。JavaScriptでは、null
とundefined
は明確に区別されています。
ここで注意が必要なのは、undefined
はリテラルではないということです。undefined
は、ただのグローバル変数で、undefined
という値を持っているだけです。
以上が、プリミティブ型の分類になります。
##オブジェクト型とリテラル
オブジェクト型には以下のものが含まれます。
- プリミティブ型以外のデータ
- オブジェクト
- 配列
- 関数
- 正規表現
- Data など...
基本的にプリミティブ型でないものは、全てオブジェクト型です。
console.log(typeof ["Messi"]);
console.log(typeof { name: "Messi" });
console.log(typeof function() {});
"object"
"object"
"function"
このように、プリミティブ型と同じようにtypeof
演算子で分類することができます。しかし、配列とオブジェクトがどちらもobject
と判定されているように、全てのオブジェクトの種類を判定することはできません。
そのため、基本的にtypeof
演算子は、プリミティブ型かオブジェクト型かを判別するために使われます。(null
のバグには注意しましょう)
オブジェクト型にもリテラルを持つものがあります。
- オブジェクトリテラル
- 配列リテラル
- 正規表現リテラル
それぞれについて説明していきます。
###オブジェクトリテラル
オブジェクトリテラルは{key: value }
の形式で書かれます。{}
は空のオブジェクトを示します。obje[key]
もしくはobj.key
とすることで、任意のプロパティ値にアクセスすることができます。ただし、プロパティ名が識別子として利用できないプロパティ名は、obj.key
で取り出すことができません。
また、オブジェクトリテラルは、Object
オブジェクトのインスタンスとして生成されます。
//どちらでも定義することができる
const obj1 = {};
const obj2 = new Object();
const obj3 = {name: "Messi"};
console.log(obj1, obj2 ,obj3);
{} {} {name: "Messi"}
基本的には、オブジェクトリテラルはオブジェクトの作成と同時に中身を定義できるので、Object
オブジェクトからオブジェクトを作成することはありません。
以下の配列や正規表現もこのオブジェクトが元になっています。
####広義のオブジェクトと狭義のオブジェクト
ここで広義のオブジェクトと狭義のオブジェクトについて説明しておきます。
先ほどから、オブジェクト型だとか、オブジェクトとか一貫性がなく使っているなと思われた方がいるかもしれません。
JavaScriptでは、一般的に連想配列と呼ばれるものもオブジェクトと呼ぶため、オブジェクト型と混同してわかりにくくなっています。ほんとに紛らわしいですよね。
基本的に、JavaScriptの書籍や記事はここの違いについて触れませんので、文脈から推論するしかありません。
それぞれについて簡単に説明すると
- 広義のオブジェクト→最終的な継承元がObjectオブジェクトであるもの
- 狭義のオブジェクト→一般的に「連想配列」と呼ばれるキーとそれに対応するプロパティの集まり
と言えます。
###配列リテラル
配列リテラルとは[1, 2, 3]
の形式で書かれます。[]
は、空の配列を示します。arr[n]
という構文で、n-1番目の要素にアクセスすることができます。
また、Array
オブジェクトのインスタンスとして生成されます。
const array1 = [];
const array2 = new Array();
const array3 = [1, 2, 3];
const array4 = new Array(1, 2, 3);
console.log(array1, array2 ,array3, array4);
[] [] [1, 2, 3] [1, 2, 3]
オブジェクトと同じような理由から、基本的にインスタンスで生成するということはありません。
###正規表現リテラル
正規表現リテラルは、/pattern/ig
の形式で書かれます。正規表現パターンでの特殊文字の使い方については、ここでは割愛しますが、他の言語とほとんど同じです。
また、RegExp
オブジェクトのインスタンスとして生成されます。
以上が、プリミティブ型とオブジェクト型の分類になります。
#プリミティブ値のリテラルでメソッドが使える理由
プリミティブ型の値は、インスタンスメソッドを持たないはずですが、普通の文字列でもメソッドを実行することができてしまいます。
これは、なぜなのでしょうか?
実は、null
とundefined
を除く全てのプリミティブ型には、それらの値を抱合する「ラッパーオブジェクト」が存在します。例えば、string型ならString
、number型ならNumber
がラッパーオブジェクトになります。ラッパーオブジェクトは、new
演算子と対応するコンストラクタ関数を利用して作成することができます。
//ラッパーオブジェクトを作成
const str = new String("Messi");
console.log(typeof str);
console.log(str.length);
"object"
5
length
は、文字の長さを返すメソッドです。str
のデータ型はオブジェクト型なので、メソッドを問題なく使うことができます。
JavaScriptでは、明示的にラッパーオブジェクトを記述せずとも、プリミティブ型のデータに対してオブジェクトのように参照する仕組みがあります。
そのため以下のようにプリミティブ型のデータに対してもメソッドを使うことができます。
//プリミティブ型の変数strを定義
const str = "Messi":
console.log(typeof str);
console.log(str.length);
"string"
5
データ型は、プリミティブ型のString型ですが、メソッドが問題なく使用できます。length
が実行できるのは、自動的に変換されたString
オブジェクトのインスタンスメソッドを実行しているからです。
#まとめ
今回は、JavaScriptのプリミティブ型とオブジェクト型についてまとめてみました。
最後まで読んでいただきありがとうございました。ではまた。