僕らは JavaScript を知らない - シンボル Symbol

初めてプログラミングを触って、それからずっと2年くらい JavaScript 使ってますが、なかなか初心者から抜け出せないなあという思いがあり、恥を忍んで JavaScript の勉強記事を書くことにしました。たぶん何回か続きます。

参考:

シンボル Symbol

ES6 (実装はもう3年以上前?) では "Symbol" と呼ばれるプリミティブ(基本)データ型が追加されました。下記のコードでは、実際にシンボルを生成しています:

var sym = Symbol( "some optional description" );

typeof sym;       // "symbol"
sym.toString();     // "Symbol(some optional description)"

var obj = { };
obj[sym] = "foobar";
Object.getOwnPropertySymbols( obj );
// [ Symbol(some optional description) ]

注意すべき特徵は、

  • new 演算子で生成するわけではない
  • 引数は任意。一度生成されたシンボルは自身のみと等しい
  • シンボルの値そのものを取得することは出来ない

ということです。ほとんどの JavaScript デベロッパーには使う機会のない代物かもしれません。具体的な使用例を以下に見ていきましょう。

列挙定数

シンボルには「一度生成されたシンボルは自身のみと等しい」という特徴があるため、次のように定数として表すことが出来ます:

const EVN_LOGIN = Symbol( "event.login" );
const EVN_LOGOUT = Symbol( "event.logout" );

Redux の ActionTypes もシンボルで定義したいところですが、残念ながら、シンボルはJSONへのパースができないため Redux DevTool との相性が悪いということもあってか、推奨されていませんでした[^1]。

オブジェクトプロパティとしてのシンボル

シンボルをオブジェクトの特別なキープロパティとして使うことも出来ます。

const MEMBER = Symbol( "a" );

function save(value) {
    return save[MEMBER] = value;
}

動作自体は以下と変わりありません:

function save(value) {
    return save.__member = value;
}

シンボルをオブジェクトのキーにした場合、シンボルは Object.getOwnPropertyNames メソッドからは秘匿されます:

var o = {
    foo: 42,
    [ Symbol( "bar" ) ]: "hello world",
    baz: true
};

Object.getOwnPropertyNames( o );   // [ "foo","baz" ]

しかし、次のメソッドを使用することでオブジェクトのシンボルプロパティを取得することができます:

Object.getOwnPropertySymbols( o );    // [ Symbol(bar) ]

Well Known Symbol

シンボル関数オブジェクトにあらかじめ定義されたシンボルのことを Well Known Symbol と呼びます。

たとえば、 iterate という Well Known Symbol は Symbol.iterate として参照可能です。これらは、 JavaScript の組み込み関数の挙動をオーバーライドするために使用することが出来ます。

また、これらを @@ プレフィックスを付けて呼ぶ(@@iterate)ことがあります。

var arr = [4,5,6,7,8,9];

for (var v of arr) {
    console.log( v );
}
// 4 5 6 7 8 9

// for ... of ループの挙動を自作のイテレータでオーバーライド
arr[Symbol.iterator] = function*() {
    var idx = 1;
    do {
        yield this[idx];
    } while ((idx += 2) < this.length);
};

for (var v of arr) {
    console.log( v );
}
// 5 7 9

Symbol.for / Symbol.keyFor

シンボルを使用する可能性のあるコードがアクセスできるように、シンボルは外部のスコープに定義しなければなりませんが、 Symbol.for メソッドを使えば、グローバルな空間における一意の文字列で管理することが可能です。

Symbol.for メソッドは、文字列を引数にとり、その文字列ですでにシンボルが定義されていればそのシンボルの値を、定義されていなければ新たに作成し、シンボルの値を返します。

また、 Symbol.keyFor メソッドでは、登録されたシンボルに渡された文字列を得ることが可能です。

var s = Symbol.for( "something cool" );

var desc = Symbol.keyFor( s );
console.log( desc );            // "something cool"

// 再びシンボルを取得
var s2 = Symbol.for( desc );

s2 === s;                       // true

Symbol については以上になります。 余裕ができたら Well Known Symbol についても詳しく書くことにしましょう。

You Don't Know JS の日本語訳プロジェクトってないのかな?あったら教えて下さい。 もしなくて、日本語訳に興味がある方がいたら一緒にやりましょう。Twitterとかで連絡下さい。スペイン語翻訳のプロジェクトは見た記憶があるので許可はもらえると思います。