JavaScriptのSymbolってなんだ

# Symbolとは

*   ECMAScript 6で追加されたプリミティブデータ型。
*  Symbol() 関数を呼び出すことで生成され、動的に無名の一意の値を生み出す。
*  well-known symbols と呼ばれる定数がある。
*  グローバルシンボルがある。

# well-known symbols と呼ばれる定数がある。
 MDN Web Docsでたまに見かける@@始まりで表現されている「@\@iterator 」等のことです。
 ※[ECMAScript](https://tc39.es/ecma262/#sec-well-known-symbols)でwell-known symbolsをいちいちSymbole.iteratorと書かなくても@\@iteratorと書いてもいいよ、ってなっています。
 well-known symbolsの詳細については、実際well-known symbolsを使用する「イテレーターとジェネレーター」などで説明します。ので、ここでは割愛します。
 
 
#  Symbol() 関数を呼び出すことで生成され、動的に無名の一意の値を生み出す。
Symbolなんて使ったこと無い。という方もいる（私も）いますが、こんな使い方があるんだー。と感じてもらい、よければ実践でも使ってもらえればと思います。

## 一意の値を生み出す。
一意の値って？？

先輩にプログラムコーディングではリレラル値の使用はしないで、定数を使え！って教わった方もいると思います。
こんな感じで

```js
// 血液型
const BLOOD_TYPE_A  = 1;
const BLOOD_TYPE_B  = 2;
const BLOOD_TYPE_O  = 3;

let aokiBloodType = BLOOD_TYPE_A;

if (aokiBloodType === BLOOD_TYPE_A) {
  console.log('ほんまにA型か？');
}
```

さらに情報を増やすためにあたらに「性別」の定数を追加します。
```js
// 血液型
const BLOOD_TYPE_A  = 1;
const BLOOD_TYPE_B  = 2;
const BLOOD_TYPE_O  = 3;

// 性別
const GENDER_MALE = 1;
const GENDER_FEMALE = 2;

let aoki = {
  gender: GENDER_MALE,
  BloodType: BLOOD_TYPE_A,
}

if (aoki.BloodType === BLOOD_TYPE_A) {
  console.log('ほんまにA型か？');
}

if (aoki.gender === GENDER_MALE) {
  console.log('ほんまに男か？');
}
```

```console:実行結果
ほんまにA型か？
ほんまに男か？
```


ま、特に問題なさそうなんですが、たまにTYPOしてしまうこともあるでしょう。

```js
// 血液型
const BLOOD_TYPE_A  = 1;
const BLOOD_TYPE_B  = 2;
const BLOOD_TYPE_O  = 3;

// 性別
const GENDER_MALE = 1;
const GENDER_FEMALE = 2;

let aoki = {
  gender: GENDER_MALE,
  BloodType: BLOOD_TYPE_A,
}

if (aoki.BloodType === BLOOD_TYPE_A) {
  console.log('ほんまにA型か？');
}

if (aoki.gender === BLOOD_TYPE_A) { // <--TYPO、間違って BLOOD_TYPE_A　を指定
  console.log('ほんまに男か？');
}

```

TYPOしていても、実行結果が同じになりました！。当然ですが。。

```console:実行結果
ほんまにA型か？
ほんまに男か？
```

これは、**潜在的なバグ**になります。今は「たまたま」期待通りの結果になりましたが、
もし、仕様変更で「BLOOD_TYPE_A=10」になったら、期待する結果にならなくなってしまいます。

**このような潜在的なバグを生む原因の一つとして、定数の値が一意の値でない**からです。
もし、定数の値が一意であれば、動作検証の時に気づくはずです。

TypeScriptでは、このような列挙値は型チェックで防げているんだろうなー。

で、完全に一意にしたいなら、Symboleを使ってもいいじゃないか？って話になります。

```js
// 血液型
const BLOOD_TYPE_A  = Symbol();
const BLOOD_TYPE_B  = Symbol();
const BLOOD_TYPE_O  = Symbol();

// 性別
const GENDER_MALE = Symbol();
const GENDER_FEMALE = Symbol();

let aoki = {
  gender: GENDER_MALE,
  BloodType: BLOOD_TYPE_A,
}

if (aoki.BloodType === BLOOD_TYPE_A) {
  console.log('ほんまにA型か？');
}

if (aoki.gender === GENDER_MALE) { 
  console.log('ほんまに男か？');
}

if (aoki.gender === BLOOD_TYPE_A) { // <--TYPO、間違って BLOOD_TYPE_A　を指定
  console.log('TYPO: ほんまに男か？');
}
```

```console:実行結果
ほんまにA型か？
ほんまに男か？
```

TYPOのif条件も正常に判断されました。

## 一意の値ということは、この値を使ってプロパティを定義をすれば、他の人が定義したプロパティとかぶることは無く安心じゃないか？？
もう一つの使い方として、プロパティ名として、Symbolを使うことも出来ます。

沢山の人でアプリを作っていると、
エンハンスなどを繰り返しているうちに、しらない間に既存のプロパティの値を上書きしてしまう！！
ようなこと。があるかも。。。。。

```js
// Aさん： オブジェクト作成
let person = {
  name: 'AOKI',
  age: 18,
  delFlg: 0, // 削除フラグ
}

// Bさん: 別の意味で削除を追加！！！！
// しかし既にあるので、上書きしてしまう！！！！
person['delFlg'] = 'A';

// Aさん: なんかの処理でdelFlg参照
// 思わぬ値で上書きされ、バッグってしまう。
if (person.delFlg === 0) {
  ...
}
```

このようなことを避ける為に

```js
// 削除フラグプロパティ名
const propDelFlg = Symbol('delFlg');

let person = {
  name: 'AOKI',
  age: 18,
  [propDelFlg]: 0, // 削除フラグ
}
```
と、プロパティ名をSymbolで定義しておけば、プロパティ名が被ることが無くなります。

また、Symbolで定義したプロパティはイテレーターでは対象外になるので、**隠しプロパティ**として扱えます！！
※Object.getOwnPropertySymbols()を使えばSymbol定義したプロパティを取得できますが、そこまでして取得する人はいないでしょう。。

```js
// 削除フラグプロパティ名
const propDelFlg = Symbol('delFlg');

let person = {
  name: 'AOKI',
  age: 18,
  [propDelFlg]: 0, // 削除フラグ
}

// プロパティ名を検索
for (let key in person) {
  console.log(key);
}
```

```console:実行結果
name
age
```

### Symbolをプロパティ名として使っても安全か？？
クローンしたりしても、消えないよね？？
検証しました。

```js
// 削除フラグプロパティ名
const propDelFlg = Symbol('delFlg');

const person = {
  name: 'AOKI',
  age: 18,
  [propDelFlg]: 0, // 削除フラグ
}

// 複製してみる。
const cloned = Object.assign( {}, person);
console.log(`cloned: ${cloned[propDelFlg]}`);

// シリアライズ、デシリアライズしてみる
const serialized = JSON.stringify(person);
const deserialized = JSON.parse(serialized);
console.log(`deserialized: ${deserialized[propDelFlg]}`);

```

```console:実行結果
cloned: 0
deserialized: undefined
```

クローンは問題なし！！
さすがにシリアライズしたら消えたので、ご注意を！！