0
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

JavaScriptのSymbolってなんだ

Posted at

Symbolとは

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

well-known symbols と呼ばれる定数がある。

MDN Web Docsでたまに見かける@@始まりで表現されている「@@iterator 」等のことです。
ECMAScriptでwell-known symbolsをいちいちSymbole.iteratorと書かなくても@@iteratorと書いてもいいよ、ってなっています。
well-known symbolsの詳細については、実際well-known symbolsを使用する「イテレーターとジェネレーター」などで説明します。ので、ここでは割愛します。

Symbol() 関数を呼び出すことで生成され、動的に無名の一意の値を生み出す。

Symbolなんて使ったこと無い。という方もいる(私も)いますが、こんな使い方があるんだー。と感じてもらい、よければ実践でも使ってもらえればと思います。

一意の値を生み出す。

一意の値って??

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

// 血液型
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型か?');
}

さらに情報を増やすためにあたらに「性別」の定数を追加します。

// 血液型
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('ほんまに男か?');
}
実行結果
ほんまにA型か?
ほんまに男か?

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

// 血液型
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していても、実行結果が同じになりました!。当然ですが。。

実行結果
ほんまにA型か?
ほんまに男か?

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

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

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

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

// 血液型
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: ほんまに男か?');
}
実行結果
ほんまにA型か?
ほんまに男か?

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

一意の値ということは、この値を使ってプロパティを定義をすれば、他の人が定義したプロパティとかぶることは無く安心じゃないか??

もう一つの使い方として、プロパティ名として、Symbolを使うことも出来ます。

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

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

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

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

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

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

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

と、プロパティ名をSymbolで定義しておけば、プロパティ名が被ることが無くなります。

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

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

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

// プロパティ名を検索
for (let key in person) {
  console.log(key);
}
実行結果
name
age

Symbolをプロパティ名として使っても安全か??

クローンしたりしても、消えないよね??
検証しました。

// 削除フラグプロパティ名
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]}`);

実行結果
cloned: 0
deserialized: undefined

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

0
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?