【備忘録】Javascript : Symbol型について


概要

JavascriptのES2015から出現したプリミティブデータ型。
下記コードを見てもらうと分かるが、シンボルの実体は「唯一無二の何か」であり、
決して競合しないユニークなキーのようなもの。

javascript.js

const sym1 = Symbol();
const sym2 = Symbol();

console.log(sym1 === sym2)  // false

※ Symbol型は初期化する際にnew演算子は不要

用途

Symbolは唯一無二なところからプロパティのキーとして使用し、使うメリットとしては名前の衝突などを防ぐことができるのだそう。
以下、使い方のコード例です。

1,プライベートメンバを定義する

Javascript.js

const SECRET_PROP = Symbol();

class App{
  constructor(hoge,foo,secret){
    this.hoge = hoge;
    this.foo = foo;
    this[SECRET_PROP] = secret; 
    // シンボル型プロパティを定義する際には定義したいオブジェクトに対して[]をつける必要がある
  }

  checkValue(val){
    return this[SECRET_PROP] == val;
  }
}

let newApp = new App("hogehoge","foofoo","this is secret");

for (let key in newApp) {
  console.log(key); // hoge , foo => ただキーを参照するだけではSECRET_PROPを取得できない
}

console.log(JSON.stringify(newApp)); // {"hoge":"hogehoge","foo":"foofoo"} => Json文字列でも同様

console.log(newApp[SECRET_PROP]); // this is secret
console.log(newApp.checkValue("this is secret")); // true 
// => クラス内部のメソッドを使用するか、定数経由でアクセスすることが可能

2, 名前の衝突回避

下記のような使い方をすることで機能の拡張時に名前の衝突を避けられます。

sample1.js

const sample = Symbol('sample');
Object.prototype[sample] = function(){
  return this.prop;
}

export {sample as default};

sample2.js

const sample = Symbol('sample');
Object.prototype[sample] = function(){
  return "this is sample2"
}

export {sample as default};

index.js

import sample1 from "sample1.js"
import sample2 from "sample2.js"

let obj = {
  prop: 'this is sample1'
}
console.log(obj[sample1]()); // this is sample1
console.log(obj[sample2]()); // this is sample2

上記のコードはモジュールを定義しているsample1.jssample2.jsindex.jsでimportしています。sample1.jssample2.jsには無名関数が同じ名前のプロトタイプ(Object.prototype[sample])に代入されています。モジュールの要素はそれぞれ一つのみなのでこれらをexport defaultしています。

もしシンボルを使っていないモジュールを複数インポートし、同じメソッド名を使っていたとしたらindex.jsでimportした際に後にimportされたsample2.jsのメソッドで上書きされてしまいますが、一番最初に申し上げたようにシンボルは唯一無二のものとして使用が可能なので同じメソッド名で同じシンボル(実際は違う)を使ってもim上書きされずにすむ。




言葉だけじゃ伝わりにくいので上書きされてしまう例も載せます。下記参考

sample1.js

Object.prototype.getProp = function(){
  return this.prop;
}

export {sample as default};
sample2.js

Object.prototype.getProp = function(){
  return "this is sample2"
}

export {sample as default};
index.js

import sample1 from "sample1.js"
import sample2 from "sample2.js"
// sample2のgetPropメソッドで上書き(Objectが何であろうと「this is sample2」としか返さない)

let obj = {
  prop: 'this is sample1'
}
console.log(obj.getProp()); // this is sample2
console.log(obj.getProp()); // this is sample2

3,互換性に対応する為

下記の記事がわかりやすいのでこちらECMAScript6にシンボルができた理由の一読をお勧めします。

以上

参考

ECMAScript6にシンボルができた理由
JavaScript初級者から中級者になろう
JavaScript互換性の要!Symbol(シンボル)について