【JavaScript】データの型と少し向き合ってみる

# JavaScriptのデータ型
「JavaScriptやPHPやRubyって型がない言語でしょ？」と認識されがちですが、これは誤り。
JavaScriptは『**動的型付け言語**』といって、プログラム実行時の値によって関数の引数や返り値の型が動的に変化するような言語なんですね。
よって、「型はあるけどそれをあまり意識することがない言語」というのが正しいかも。
一方でTypeScriptなどのように、プログラム実行前に関数の引数や返り値の型が予め決まっているのが『**静的型付け言語**』。

試しにそれぞれの言語で、`year`という変数に数値型の`2022`と文字列型の`2022`を代入してみる。

```javascript
/* JavaScript */
let year = 2022;
year = '2022';

/* TypeScript */
let year: number = 2022;
year = '2022'; // Type 'string' is not assignable to type 'number'.
```
JavaScriptは数値型でも文字列型でも代入が可能。（＝**変数が持つ値の型は動的に変わる**）
TypeScriptは数値型で変数宣言したので文字列を代入しようとするとエラーが出る。（＝**変数が持つ値の型は宣言した型のみ**）

JavaScriptもちゃんとデータに型を持っているんだな〜というのが分かったところで、ここからが本題。
データ型は大きく分けて**プリミティブ型**と**オブジェクト型**に分けられる。

## プリミティブ型とオブジェクト型
### プリミティブ型
オブジェクトでなく、インスタンスメソッドを持たないデータ型。

- **論理型（Boolean）**：true/falseの真偽値を扱う。
- **null型**：値が存在しないことを明示的に表す。
- **undefined型**：変数に値が代入されていないことや、アクセスしようとしたプロパティが存在しないことを表す。
- **数値型（Number）**：整数、浮動小数点、Nan（非数）、Infinity（無限大）を扱う。最大値は2^51-1。
- **長整数型（BigInt）**：数値型で扱うことが出来る値を超える数値を扱う。ES2020で追加された。
- **文字列型（String）**：テキストデータを表す。
- **シンボル型（Symbol）**：「シンボル値」という固有の識別子を表す。

```javascript
const message = 'Hello World!!';
typeof(message) // => string
```

### オブジェクト型
プロパティ（キーと値の関連付け）から成る集合体で、プリミティブ型以外のものを指す。

```javascript
const message = { text: 'Hello World!!' };
typeof(message) // => object
```

## リテラル
プリミティブ型のデータや一部のオブジェクトに関しては、「こうやって書けばこのデータ型として定義して扱うことが出来るよ」という構文があって、それを**リテラル**という。

**プリミティブ型のリテラル**

|型|リテラル|記法|
|---|---|---|
|論理型|真偽値リテラル|**true** または **false**|
|null型|nullリテラル|**null**|
|undefined型|-|-|
|数値型|数値リテラル<br>浮動小数点リテラル|数字の組み合わせ （例）2, 99, 1000<br>小数点を用いた数字の組み合わせ （例）3.14|
|長整数型|数値リテラル|数字の後に **n** を付ける （例）100n|
|文字列型|文字列リテラル|**''**（シングルクォート）または **""**（ダブルクォート）で囲む|

**オブジェクト型のリテラル**

|種類|リテラル|記法|
|---|---|---|
|Objectオブジェクト|オブジェクトリテラル|**{ key: value }** （ **{}** は空オブジェクトを表す）|
|Arrayオブジェクト|配列リテラル|**[x, y, z, ...]** （ **[]** は空配列を表す）|
|RegExpオブジェクト|正規表現リテラル|**/pattern/frags** （例）/[a-z]+/i

## Objectオブジェクト？
オブジェクト型のObjectオブジェクトって、何ともややこしい。
どうやらJavaScriptには「広義のオブジェクト」と「狭義のオブジェクト」があるらしい。

「狭義のオブジェクト」にあたるのが、オブジェクトリテラルの構文でkeyとvalueの形で定義した、オブジェクト型の1種類としてのObjectオブジェクト。
「広義のオブジェクト」にあたるのが、プリミティブ型以外の全てのデータを指すオブジェクトという型。

`__proto__`というプロパティから、各オブジェクトの継承元を辿ってみる。

```javascript
/* Objectオブジェクト */
const body = { height: 165, weight: 50 };
body.__proto__ // => {}
body.__proto__.constructor // => [Function: Object]
body.__proto__.constructor.name // => 'Object'

/* Arrayオブジェクト */
const animals = ['cat', 'dog', 'hamster'];
animals.__proto__ // => []
animals._proto__.__proto__ // => {}
animals._proto__.__proto__.constructor // => [Function: Object]
animals._proto__.__proto__.constructor.name // => 'Object'

/* RegExpオブジェクト */
const regex = /[a-z]+/i.;
regex._proto__ // => RegExp {}
regex._proto__.__proto__ // => {}
regex._proto__.__proto__.constructor // => [Function: Object]
regex._proto__.__proto__.constructor.name // => 'Object'
```

このように、広義のオブジェクトは、Objectという標準組み込みオブジェクトを最終的な継承元に持っている。

## 「プリミティブ型はインスタンスメソッドを持たない」について
このようなコードを書いたことがある。

```javascript
const text = 'I live in Tokyo.';
text.replace('Tokyo', 'Osaka'); // => 'I live in Osaka.'
```
あれ？string型のデータが代入されているプリミティブ型の`text`に対して`replace()`メソッドが使用出来ている？
「プリミティブ型はインスタンスメソッドを持たない」のでは？

これには**ラッパーオブジェクト**というものが関係しているらしい。

### ラッパーオブジェクト
プリミティブ型のデータを内包するオブジェクトのこと。
`null型`と`undefined型`を除くプリミティブ型のデータは、ラッパーオブジェクトに内包されている。

|型|ラッパーオブジェクト|
|---|---|
|論理型|Boolean|
|数値型|Number|
|長整数型|BigInt|
|文字列型|String|
|シンボル型|Symbol|

つまり、先ほどのコード例でプリミティブ型のデータに対してメソッドが使用出来ていたのは、

```javascript
const text = 'I live in Tokyo.';
```

ここの部分が

```javascript
const text = new String('I live in Tokyo.');
```
「お、プリミティブ型のデータにメソッド使いたいの？使えるようにStringオブジェクトにしておくねー」という風に、JavaScript側で自動変換されていたからなんですね。JavaScriptでは、プリミティブ型の値に対してプロパティアクセスするとき、データ型に対応するラッパーオブジェクトに自動で変換してくれるのだそう。
こういったことも含めて、JavaScriptは型を意識せず使えるんだなーと。

# まとめ
- JavaScriptは動的型付け言語であり、データ型は『プリミティブ型』と『オブジェクト型』に大別される
- 『リテラル』というデータ構文を使うことでそれぞれのデータ型を扱える
- プリミティブ型は『ラッパーオブジェクト』に内包されており、必要に応じて自動でオブジェクトに変換される