JavaScriptとRubyのSymbolの違い

シンボルとは

Ruby

Rubyの内部実装では、メソッド名や変数名、定数名、クラス名など の`名前'を整数で管理しています。これは名前を直接文字列として処理するよりも 速度面で有利だからです。そしてその整> 数をRubyのコード上で表現したものがシンボルです。

シンボルは、ソース上では文字列のように見え、内部では整数として扱われる、両者を仲立ちするような存在です。

名前を管理するという役割上、シンボルと文字列は一対一に対応します。 また、文字列と違い、immutable (変更不可)であり、同値ならば必ず同一です。

class Symbol (Ruby 2.3.0)

stringとは違い、同じ名前のシンボルであれば、同一のオブジェクトになるわけですね。
サンプルを見てみましょう

まず、stringであれば、別個に定義したstr1str2はそれぞれオブジェクトが作られるので、 二つは等価にはなりません。

str1 = "foo"
str2 = "foo"

p str1.equal?(str2)
# => false

対してSymbolであれば、バラバラに定義しても同一のオブジェクトになるので、
二つは等価になります。

sym1 = :foo
sym2 = :foo

p sym1.equal?(sym2)
# => true

JavaScript

symbolは、ユニークで不変なデータ型で、オブジェクトのプロパティ識別子として使われたりします。symbolオブジェクトは、Symbolプリミティブデータ型をラップした暗黙的なオブジェクトです。

Symbol - JavaScript | MDN

JavaScriptのシンボルは毎回新しいSymbolオブジェクトを生成します。 そのため、以下はfalseになります。

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

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

一方、 文字列比較は単純に同一の値か否かなので以下は 等しくなります。

const str1 = "foo"
const str2 = "foo"

console.log(str1 === str2)
// -> true

つまり、Rubyとは等価性の判定が逆になります。

JavaScriptRubyのようにSymbolを扱うには

Symbol.for(key)を使うことでRubyのように同一のオブジェクトを参照できます。

与えられたkeyで存在するシンボルを検索し、見つかればそれを返します。もしくは、グローバルsymbolレジストリにこのkeyで新しいsymboleを生成します。

const sym3 = Symbol.for("foo")
const sym4 = Symbol.for("foo")

console.log(sym3 === sym4)
// -> true

なお、Symbol.for(key)Symbol(key)で作ったオブジェクトとは等価にならないのでご注意を

const sym5 = Symbol("foo")
const sym6 = Symbol.for("foo")

console.log(sym5 === sym6)
// -> false