Symbolについて

ES2015(ES6)で追加されたgenerators(yield)
generatorsを使うにはSymbol、Iteratorを理解しておいた方がいいので順番に説明していきます
今回は「Symbol」についてです

概要

ES2015で追加された新しいプリミティブです
数値でも文字列でも真偽値でもない値ですが
文字列のようにオブジェクトのプロパティのキーとして使えます
またObject.keys、Object.getOwnPropertyNamesでも列挙できないという特徴を持っています

作り方

var s = Symbol();
var s = Symbol('foo'); // 引数に文字列を渡すことでSymbolの説明を追加することができる

念のためtypeof演算子で確認するとSymbolであることがわかります

typeof s; // "symbol"

またnew演算子付きで呼ぶとType Errorとなるので注意

var s = new Symbol(); // Type Error

使い方

Symbolはオブジェクトのプロパティのキーにすることができます

var obj = {};
var s = Symbol();
obj[s] = 'hoge';
console.log(obj[s]); // hoge

ちなみに、ES2015のComputed Property Namesを使えば下記のように書けます

var s = Symbol();
var obj = {
    [s]: 'hoge'
};
console.log(obj[s]); // hoge

シンボルは暗黙的に文字列変換されるわけではないので、下記の場合undefinedになります

console.log(obj[s.toString()]); // undefined

特徴

Symbolは毎回異なるシンボルが作成されます

Symbol('foo') === Symbol('foo'); // false

この特徴を活かして、ES2015は既存のコードに影響がでないように
機能を追加できるようにしたようです

例えば、

function test(obj, value) {
    obj._value = value;
}

上記のようなコードの場合、外部から勝手にobjのプロパティを上書きされて全く別のものになったり
キーがシンボルではないので普通に外部から参照できてしまいます

↓このようにすることで、外部からいじられる心配も、参照される心配もありません

var obj = {};
var _value = Symbol();
function test(obj, value) {
    obj[_value] = value;
}
test(obj, 123);

キーがシンボルなので同じキーを外部から作れないためです

ただし、Object.getOwnPropertySymbolsでシンボルを取得できてしまうので
完全に隠蔽できるわけではないです

console.log(obj[Object.getOwnPropertySymbols(obj)[0]]); // 123

登録

シンボルは毎回異なるシンボルが作成されますが
Symbol.forを使ってシンボルを登録すると共有することが可能です

Symbol.for

引数で与えられたキーでランタイム全体のシンボルレジストリ内に存在しているシンボルを検索し、見つかった場合それを返します。さもなければ、新しいシンボルがこのキーでグローバルシンボルレジストリ内に生成されます。

↓こんな感じでシンボルを共有できます

var s1 = Symbol.for('foo');  // ここでは新しく作成される
var s2 = Symbol.for('foo'); // 作成されているシンボルを参照
var s3 = Symbol('foo'); // これはグローバルシンボルレジストリに生成されてないシンボルの作成
s1 === s2; // true
s1 === s3; // false

Well-Known Symbols

ウェルノウンシンボルという特別なシンボルがあります
「特別なシンボル」と書くと、何かすごい機能があるように思えますが、ごく普通のシンボルです
JavaScriptの内部処理で参照されるシンボルです

Well-Known Symbols

このウェルノウンシンボルの中にSymbol.iteratorというシンボルがあります
IteratorはこのSymbol.iteratorを使うことで実現します

Iteratorについての説明は次の回