【JavaScript】データ型

### データ型とは

データの種類のこと。
例えば、「データのかた」、「dataType」は文字列、1、-1、3.14は数値型、true (真) / false (偽) は論理型に分類される。

### データ型の分類

データ型は、大きく`基本型`と`参照型`とに分類できる。
両者の違いは値を変数に格納する方法。

- 基本型の変数
    値そのものが直接に格納される
- 参照型の変数
その参照値が (値を実際に格納しているメモリ上のアドレスのようなもの) を格納する


<table>
  <caption>データ型一覧</caption>
  <thead>
    <tr>
      <th>分類</th>
      <th>データ型</th>
      <th>概要</th>
    </tr>
  </thead>
  <tr>
    <td rowspan="6">基本型</td>
    <td>数値型 (number)</td>
    <td>$\pm5\times10^{-324}$ 〜 $1.7976931348623157\times10^{308}$ (整数値では$-(2^{53}-1)$) 〜 $2^{53}-1$)</td>
  </tr>
  <tr>
    <td>長整数型 (bigint)</td>
    <td>number型を超える任意精度の整数　（ES2020で導入）</td>
  </tr>
  <tr>
    <td>文字列型 (string)</td>
    <td>クォートで囲まれた0個以上の文字列の集合</td>
  </tr>
  <tr>
    <td>論理型 (boolean)</td>
    <td>true (真) / false (偽)</td>
  </tr>
  <tr>
    <td>シンボル型 (symbol)</td>
    <td>一意の値 (シンボル) を表現</td>
  </tr>
  <tr>
    <td>特殊型 (null / undefined)</td>
    <td>値が空、未定義であることを表す</td>
  </tr>
  <tr>
    <td rowspan="3">参照型</td>
    <td>配列 (array)</td>
    <td>データの集合 (各要素にはインデックス番号でアクセス可能)</td>
  </tr>
  <tr>
    <td>オブジェクト (object)</td>
    <td>データの集合 (各要素には名前でアクセス可能)</td>
  </tr>
  <tr>
    <td>関数 (function)</td>
    <td>一連の処理 (手続き) の集合</td>
  </tr>
</table>

### 論理リテラル (boolean)

論理型は、真　(正しい)　か偽　(まちがい)　のいずれかの状態のみを持つ。
論理型を表す値を`論理値`といい、それぞれtrue (真)、fasle (偽) というキーワードで表現できる。
大文字/小文字を区別するので、TRUE/FALSE、True/Falseなどは不可。

#### 暗黙的なtrue / false

論理値を必要とする文脈で、以下の値を暗黙的にfalseと見なす。
`falseキーワードだけがfalseを表すわけではない。`


- 空文字 ('')
- ゼロ値 (0、-0、0n)
- null / undefined、NaN (Not a Number)

これらの値のことを`falsyな値`と呼ぶ。
逆に上記で示した以外の直はすべてtrueと見なされ、`truthyな直`と呼ぶ。

### 数値リテラル (number)

数値リテラルは、`整数リテラル`と`浮動小数点リテラル`に分類できる。

```text:数値リテラル分類
数値リテラル
└───　 整数リテラル
│    └───　10進数リテラル ・・・ 0、１、１００
│    └───　16進数リテラル ・・・ 0xCC55CC、0xFFffFF
│    └───　8進数リテラル  ・・・ 0o600、0o666、0o777
│    └───　2進数リテラル  ・・・ 0b11、0b101
└─── 浮動小数点数リテラル ・・・ 1.1、3.14e5
```

#### 整数リテラル

2進数 / 8進数 / 16進数で表現する場合は、リテラルの頭にそれぞれ 0b / 0o / 0x をつける必要がある。
2進数では 0 〜 1 の値を、8進数では 0 〜 7 の値を、16進数では  0 〜 9 の数値に加えて、A (a) 〜 F (f) までの英字を利用できる。
それ以外の値をを指定した場合には、「Invalid or unexpected token」 (不正な文字) のようなエラーとなる。

:::note info
リテラルの接頭辞の「0b」、「0o」、「0x」は大文字小文字を区別しないので、それぞれ「0B」、「0O」、「0X」でも違いはない。
だたし、大文字とO（オー）は数値の0(ゼロ)と区別がつきにくいため、一般的に「0o」のような小文字での表記がおすすめ。
:::

#### 浮動小数点数リテラル

小数の表示には指数表現が存在する。
`指数表現`は、`<仮数部>e<符号><指数部>`の形式で表される。また、`<仮数部>×10<符号><指数部>乗`で本来の値に変換できる。
例えば、「3.14e5」は「$3.14\times10^{5}$」を表す。指数を指す「e」は大文字でも小文字でも構わない。

#### 数値セパレーター(ES2021で導入)

桁数の大きい数値を読みやすくするために、数値に桁区切り文字(_)を使用して表現するのが`数値セパレーター`。

```javascript:数値セパレーター
// 整数
let i = 1_234_567
// 16進数
let j = 0x123_456
// 浮動小数点
let k = 1.123_456_789
// 指数表現
let l = 0.123_456e10
```

数値セパレーターは、人間の可読性を向上させるための記号なので、挟み方は比較的に自由。

- 12_34_56 ➡︎ 2桁区切り
- 1_23_456 ➡︎ 異なる桁区切り 

ただし、以下のような記述の場合は、数値を区切るという目的から外れているので不可。

- 0x_1234、0_x1234 ➡︎ 数値プレフィックスの直後、途中
- 1._234 ➡︎ 小数点の直後
- _1234 ➡︎ 数値リテラルの先頭
- 12__34 ➡︎ 専属したセパレーター

### 文字列リテラル (string)

文字列リテラルを表すには文字列全体をシングルクォート (') またはダブルクォート ("") でくくる。
クォート文字が文字列の開始と終了を表す。

```javascript:文字列リテラル
'JavaScriptの文字列'
"JavaScriptの文字列"
```

よって、文字列リテラルには「'」、「"」そのものを含めることはできない。例えば、以下のコードは文法エラーとなる

```javascript:文字列リテラルの文法エラー
console.log('It's JavaScript');
```

2個目のシングルクォートでリテラルが終了してしまうため、以降の文字列は正しく解釈できないので、文法エラーとなる。
このような場合は、以下の方法で対処できる。

#### (1) 文字列に含まれないクォートで括る

文字列にシングルクォートを含む場合は、全体をダブルクォートでくくる。

```javascript:文字列リテラルのダブルクォートくくり
// 文法エラーになる
console.log('It's JavaScript'); // ・・・ ①

// ① は文字列にシングルクォートが含まれるので、全体をダブルクォートでくくる
console.log("It's JavaScript");
```

逆に文字列にダブルクォートを含む場合は、全体をシングルクォートでくくる。

```javascript:文字列リテラルのシングルクォートくくり
// 文法エラーになる
console.log("You are a "Good" engineer"); // ・・・ ①

// ① は文字列にダブルクォートが含まれるので、全体をシングルクォートでくくる
console.log('You are a "Good" engineer');
```

#### (2) クォート文字をエスケープ処理する

（１） の方法ではシングルクォート / ダブルクォートを同時に含めることができない。例えば、以下のコードは文法エラーになる。

```javascript:文字列リテラルの文法エラー
// 文法エラーになる
console.log('You're a "Good" engineer');

// 以下のように書き換えても、文法エラーになる
console.log("You're a "Good" engineer");
```

このような場合は、文字列に含まれるクォート文字を`エスケープ処理`する。
エスケープ処理とは「あるコンテキストで意味のある文字を、決められたルールに従って無効化する」こと。
ここでの、「意味のある文字」とは、文字列リテラルの開始 / 終了を意味するクォートになる。
以下のように表すことでクォート文字をエスケープできる。

```javascript:文字列リテラルのエスケープ
// シングルクォートのエスケープ
console.log('You\'re a "Good" engineer');

// ダブルクォートのエスケープ
console.log("You're a \"Good\" engineer");
```

「'」「"」は (文字列リテラルの開始 / 終了ではなく) ただの「'」「"」と見なされるので、正しく動作する。


#### エスケープシーケンス

「\」で表される文字のことを`エスケープシーケンス`と呼ぶ。
クォート文字のエスケープ処理に利用するほか、改行、タブ文字のような特殊な意味を持つ文字を表すために利用する。
エスケープシーケンスには、以下のようなものがある。

| 文字       | 概要                                                    |
|:----------|:--------------------------------------------------------|
| \b        | バックスペース                                             |
| \f        | 改ページ                                                 |
| \n        | 改行 (LF: Line Feed)                                    |
| \r        | 復帰 (CR: Carriage Return)                              |
| \t        | タブ文字                                                 | 
| \v        | 垂直タブ                                                 |
| \\        | バックスラッシュ                                            |
| \\'        | シングルクォート                                            |
| \\"        | ダブルクォート                                             |
| \xXX      | Latin-1文字 (XXは16進数)。例: \x61 (a)                    |
| \uXXXX    | Unicode文字 (XXXXは16進数値)。例: \u3042 (あ)              |
| \u{XXXXX} | 0xffff (4桁の16進数) を超えるUnicode文字。例: \u{20b9f} （叱） |

#### テンプレート文字列　（ES2021で導入）

テンプレート文字列を用いることで、以下のような文字列表現が可能になる。

- 複数行にまたがる (=改行文字を含んだ) 文字列
- 文字列への変数の埋め込み

テンプレート文字列では、シングルクォート / ダブルクォートの代わりに、「`」で文字列をくくる。

```javascript:テンプレート文字列
let name = 'JavaScript'
// テンプレート文字列
let templateString = `Hello, ${name}!
You're a good engineer!`
console.log(templateString);
```

```javascript:出力結果
Hello, JavaScript!
You're a good engineer!
```

「'」「"」では「\n」 (エスケープシーケンス)　で表されなければならなかった改行文字を、テンプレート文字列ではそのまま表現できる。さらに、＄{...} の形式で変数 (式)　を文字列に埋め込むことが可能。
従来であれば、変数とリテラルを「＋」演算子で連結するしかなったところなので、コードがシンプルになる。

### 配列リテラル (array)

`配列`とは、データの集合のこと。
1つの変数に対して複数の値を格納できる。
配列を利用することで、お互いに関連する値の集合を1つの名前で管理できるので、コードをすっきり表現できる。

#### 配列の基本

配列の作成
`変数名 = [値1, 値2, ...]`

配列は、カンマ区切りの値をブラケット ([...]) でくくった形式で表現する。値の型はお互いに異なっていても構わないが、一般的に1つの配列内では文字列なら文字列で統一するのが普通。空の配列を生成するならば、単に[]とする。
ブラケット ([...]) でくくられた部分は`インデックス番号`、または`添字`と呼ばれ、配列の何番目の要素を取り出すのかを表す。インデックスは0から始まるので1番最初の要素は0番目になる。
配列のサイズを超えたインデックス番号を指定した場合には、未定義を意味するundefinedという値が返却される。

配列要素へのアクセス (ブラット構文)
`配列名[インデック番号]`

```javascript:配列の具体例
// 配列の作成
let data = ['JavaScript', 'Python', 'Ruby'];
// 配列の0番目の要素
console.log(data[0]);
// 配列の1番目の要素
console.log(data[1]);
// 配列の2番目の要素
console.log(data[2]);
// 配列の100番目の要素, サイズを超えたインデックス番号で要素が存在しない
console.log(data[100]);
```

```javascript:出力結果
JavaScript
Python
Ruby
undefined
```

ブラケット構文を利用することで、配列要素を書き換えたり、新たに要素を設定することもできる。

```javascript:配列の具体例
// 配列の作成
let data = ['JavaScript', 'Python'];

console.log('---------- 配列要素書き換え、新規追加前 ----------');
// 配列の0番目の要素
console.log(data[0]);
// 配列の1番目の要素
console.log(data[1]);

console.log('---------- 配列要素書き換え後 ----------');
// 配列の0番目の要素の書き換え
data[0] = 'Java'
// 配列の1番目の要素の書き換え
data[1] = 'Rust'
// 配列の0番目の要素
console.log(data[0]);
// 配列の1番目の要素
console.log(data[1]);

console.log('---------- 配列要素新規追加後 ----------');
// 配列の100番目に新しい要素を追加
data[100] = 'TypeScript'
// 配列の100番目の要素
console.log(data[100]);
```

```javascript:出力結果
---------- 配列要素書き換え、新規追加前 ----------
JavaScript
Python
---------- 配列要素書き換え後 ----------
Java
Rust
---------- 配列要素新規追加後 ----------
TypeScript
```

飛び版で要素を追加した場合、間の要素が空の配列が生成される (これを`疎な配列`と呼ぶ) 。

```javascript:疎な配列
// 配列の作成
data = [1, 2, 3]
// 配列サイズを超える、インデックス番号に要素を追加
data[5] = 5

// 配列要素を全て出力
for (let i = 0; i < data.length; i++) {
  console.log(data[i]);
}
```

```javascript:出力結果
1
2
3
undefined
undefined
5
```

### 入れ子の配列

配列の要素として格納できるのは、数値や文字列だけではない、任意の型、例えば配列そのものを格納しても構わない。

```javascript:入れ子配列
let data = [
  ['JavaScript', 'js'],
  ['Python', 'py'],
  ['Ruby', 'rb'],
];
console.log(data[1][0]);
```

```javascript:出力結果
Python
```

入れ子の配列から値を取り出すには、ブラケットも入れ子の数だけ列挙して、それぞれの階層のインデックス番号を指定する。

### オブジェクトリテラル (object)

インデックス番号でアクセスできる配列に対して、`オブジェクト`は名前をキーにアクセスできる配列。言語によっては、ハッシュ、連想配列などと呼ばれる仕組み。
通常の配列がインデックス番号しかキーにできないのに対して、オブジェクトでは文字列をキーにアクセスできるため、データの可読性が高いのが特徴。
配列内の個々のデータを「要素」と呼んでいたのに対して、オブジェクト内の個々のデータは`プロパティ`と呼ぶ。プロパティには、文字列や数値などのほかに、関数を格納することもできる。関数が格納されたプロパティのことは、特別に`メソッド`と呼ぶ。

オブジェクトの作成
`変数名 = { プロパティ名1: 値1, プロパティ名2: 値2, ... }`

オブジェクト配下のプロパティにアクセスするにも、ドット演算子 (.) による方法とブラケット構文 ([...]) による方法の2つがある。

```text:オブジェクトのプロパティアクセス
ブクジェクト名.プロパティ名 ・・・ ドット演算子
ブクジェクト名['プロパティ名'] ・・・ ブラケット構文
```

ドット演算子 / ブラケット構文を利用することで、オブジェクトに新たなプロパティを追加したり、既存の要素を書き換えたりすることもできる。

```javascript:オブジェクトの具体例
// オブジェクトの作成
let language = {
  javaScript: 'Hello JavaScript',
  python: 'Hello Python',
  ruby: 'Hello Ruby',
};

console.log('---------- ドッド演算子 / ブラケット構文でプロパティにアクセス ----------');
// ドット演算子でプロパティにアクセス
console.log(language.javaScript);
// ブラケット構文でにアクセス
console.log(language['python']);

console.log('---------- プロパティの書き換え ----------');
// 既存のプロパティの値の書き換え
language.ruby = 'rb';
console.log(language.ruby);

console.log('---------- プロパティの新規追加 ----------');
// プロパティを新規追加
language.rust = 'Hello Rust';
console.log(language.rust);
```

```javascript:出力結果
---------- ドッド演算子 / ブラケット構文でプロパティにアクセス ----------
Hello JavaScript
Hello Python
---------- プロパティの書き換え ----------
rb
---------- プロパティの新規追加 ----------
Hello Rust
```

#### ドット演算子とブラケット構文の違い

ドット演算子とブラケット構文はお互いに置き換えることとは可能だが、例外がある。

```javascript:ドット演算子とブラケット構文の違い
let obj = {}
// エラーになる, 識別名の命名規則に則らない
obj.123 = 'NG!'
// プロパティ名はあくまで文字列
obj['123'] = 'OK!'
```

ドット演算子では、プロパティ名は識別子と見なされるので、識別子の命名規則にの則らない「123」のような名前は使えない。しかし、ブラケット構文では、プロパティ名はあくまで文字列なので、このような制限はない。

#### 入れ子オブジェクト

オブジェクトもまた、そのプロパティとしてオブジェクトを持てる。
入れ子のプロパティにアクセスするには、ドット演算子を連ねるか、ブラットを列記する。

```javascript:オブジェクトの入れ子
let engineer = {
  name: 'JavaScript Engineer',
  skill: {
    frontend: 'JavaScript',
    backend: 'Java'
  },
};

// ブラケット構文でプロパティにアクセス
console.log(engineer.name)
// ドット演算子でプロパティにアクセス
console.log(engineer['skill']);
console.log(engineer.skill.frontend);
console.log(engineer.skill.backend);
```

```javascript:出力結果
JavaScript Engineer
{frontend: 'JavaScript', backend: 'Java'}backend: "Java"frontend: "JavaScript"
JavaScript
Java
```

### 関数リテラル

`関数`とは、`なにかしらの入力値 (引数) を与えられることによって、あらかじめ決められた処理を行い、その結果 (戻り値) を返す仕組み`
JavaScriptでは、関数もデータ型の一種として扱われる。
定義した関数を変数に格納することで、同じコードを繰り返し記述することなく、複数の場所で同じコードを呼び出すことで再利用が可能。

```javascript:関数の具体例
// 変数に無名関数を格納
let functionCall = (val) => {
  console.log('関数の引数 ⇨ ', val);
};

// 変数に格納した関数を呼び出す
functionCall('Hello JavaScript');
```

```javascript:出力結果
関数の引数 ⇨  Hello JavaScript
```

### 未定義値 (undefined) とヌル値 (null)

「何もない」を表す特別な値としてundefinedとnullがある。

#### 未定義値 (undefined)

`未定義値 (undefined)`は、ある変数の値が定義されていないことを表す値で、以下の場合で使われる。

- ある変数が宣言済みだが値がセットされていない
- オブジェクトで未定義のプロパティを参照しようとした
- 関数で値が返却されなかった

```javascript:undefinedの具体例
let x;
// 値が設定されてない
console.log(x);

let obj = { y: 'Hello!' };
// プロパティが存在しない
console.log(obj.z);
```

```javascript:出力結果
undefined
undefined
```

#### ヌル (null)

`null`は該当する値がない、存在しないを意味する。undefinedは「定義されていない、そもそも参照することを想定していない」とういう状態を表すのに対して、nullは「空である」という状態を明示するための値

```javascript:nullの具体例
let msg = 'Hello, JavaScript!';
// 変数に空を代入
msg = null;
console.log(msg);
```

```javascript:実行結果
null
```

### Symbom (シンボル)

Symbolデータ型の値は`シンボル値`と呼ばれる。`シンボル値`はSymbol関数を呼び出すことで生成される無名な一意な値。ブジェクトプロパティとして使用することができる。

```javascript:Symbolの具体例
let sym1 = Symbol('Sym');
let sym2 = Symbol('Sym');

let obj = {};
let symKey = Symbol('key');
obj[symKey] = 'JavaScript';

console.log('---------- シンボル値の比較 ----------');
console.log(sym1 === sym1);
// 同じ説明文でシンボルを生成してもお互い異なる値を持つ
console.log(sym1 === sym2);

console.log('---------- オブジェクトのプロパティがシンボル値 ----------');
// オブジェクトのプロパティ
console.log(obj);
console.log(obj[symKey]);
```

```javascript:実行結果
---------- シンボル値の比較 ----------
true
false
---------- オブジェクトのプロパティがシンボル値 ----------
{Symbol(key): 'JavaScript'}
JavaScript
```

#### 文字列に「自動変換」されない
シンボルは文字列に「自動変換」されない。例えば、`alert`はほぼすべての値で表示することができ、動作するがシンボルの場合は文字列への変換処理を行わないと動作しない。

```javascript:シンボルを文字列に変換しない例
let sym = Symbol('Sym');
alert(sym);
```

```javascript:実行結果
Uncaught TypeError: Cannot convert a Symbol value to a string
```

```javascript:シンボルを文字列に変換する例
let sym = Symbol('Sym');

// 文字列への変換は.toString()を呼び出す必要がある
alert(sym.toString());
// symbol.description プロパティを使用して、説明文を取得することができる
alert(sym.description);
```

**実行結果**
![Symbol - 2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/310302/23cacb67-f098-8598-bd95-6c70a94b8f60.png)

![Symbol - 1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/310302/f583fa58-3982-2b31-c435-58cd1acca413.png)