【JavaScript】Privateなプロパティの実装に出てくるSymbolは何をしているのか

はじめに

こんにちは、SHOJIです。

JavaScript(ES6)でプライベートプロパティの実装について調べるとSymbolを使った方法がよく出てきます。

本記事では、Symbolとは何なのか?について記載します。

Symbolを用いたPrivate Propertiesの参考記事

stackoverflow.com

Symbolの使用例

Symbolを使わない例(Privateプロパティになっていない状態)

// 即時関数のため、DogにはDogクラスの定義が格納される
var Dog = (() => {
    const propSound = "propSound"; // プロパティとしてpropSoundを使用

    class Dog{       
        constructor(sound){
            this[propSound] = sound;
        }
        calls(){
            console.log(this[propSound]);
        }
    }
    
    return Dog;
})();

const dog = new Dog("bow-wow"); // Dogクラスのインスタンスを生成(鳴き声にbow-wowを設定)
console.log(dog);               // Dogインスタンスの中身:Dog { propSound: 'bow-wow' }

dog["propSound"] = "woof"; // DogのpropSoundプロパティをwoofに変更する
dog.calls();               // woofが出力される

上記のコードはdogインスタンスのsoundを変更できますが、次のSymbolを使う例では外からプロパティを変更できません。

Symbolを使う例(Privateプロパティになった状態1

// 即時関数のため、DogにはDogクラスの定義が格納される
var Dog = (() => {
    const propSound = Symbol(); // プロパティとしてSymbol()を使用

    class Dog{       
        constructor(sound){
            this[propSound] = sound;
        }
        calls(){
            console.log(this[propSound]);
        }
    }
    
    return Dog;
})();

const dog = new Dog("bow-wow"); // Dogクラスのインスタンスを生成(鳴き声にbow-wowを設定)
console.log(dog);               // Dogインスタンスの中身:Dog { [Symbol()]: 'bow-wow' }

dog[Symbol()] = "woof"; // DogのSymbol()プロパティをwoofに変更する
dog.calls();            // bow-wowが出力される(変更できていない)

処理終了時点でdogオブジェクトがどうなっているかというと、Symbolが二つ存在しています。

console.log(dog); // Dog { [Symbol()]: 'bow-wow', [Symbol()]: 'woof' }

Symbolは何をしているのか

mozillaのSymbolのページ(https://developer.mozilla.org/ja/docs/Glossary/Symbol)ではこのような説明がされています。

動的に無名の一意の値を生み出します。シンボルはオブジェクトプロパティとして使用されることがあります。

なるほど、Symbolは必ず一意の値を生成するから

dog[Symbol()] = "woof"; // DogのSymbol()プロパティをwoofに変更する

このようにしてもdogインスタンスが元々持っていたSymbolプロパティとは別物とみなされたんですね。これなら処理終了時点でdogオブジェクトにSymbolプロパティが二つ存在している(ように見える)ことにも納得がいきます。

ちなみに、Symbolは文字列を引数に取ることができます。

const propSound = Symbol("sound property");

この引数はあくまで説明書きとしてしか機能しないので、同じ文字列を引数にしたSymbolであっても別の値が生成されます。

console.log(Symbol("sound property") === Symbol("sound property")) // false

  1. アクセスする手段はあるので厳密にはPrivateではないです。