[JavaScript] ObjectのkeyにあらゆるObjectを使ってみる

 JavaScriptでは、ObjectのKeyにObjectを使うことができます。何が起きるのが想像もつきません。やってみましょう。

Objectを渡してみる

const a = {};
a[{}] = "key is an object";
console.log(a[{}]);  // -> "key is an object"

 なんと要素が取得できてしまいました。続いてもう少し実験を続けてみます。

const a = {};
a[{}] = "key is an object";
console.log(a[{hoge: "fuga"}]);  // -> "key is an object"

 空のObject({})をキーとして値を設定しましたが、取得する段階で全く別のObjectである{hoge: "fuga"}を渡しても値が取得できてしまいます。これは一体どういうことなのでしょうか。

ObjectはKeyとして使うと、内部的にtoString()される

 ごく単純な話ですが、これはkeyを設定するときに内部的にtoString()されているに過ぎません。ObjectをtoString()すると"[Object object]"という文字列になります。これを実証してみましょう。

const a = {};
a[{}] = "key is an object";
console.log(a["[Object object]"]);  // -> "key is an object"

 値が取得できたことがわかります。ObjectをObjectのKeyとして設定するのは危険そうです。

Arrayを渡してみる

 それなら、Arrayは順序保証もあるし、toString()でも意味のある値が返されるので、使ってもよいのでは?と考えてみます。やってみましょう。

const a = {};
a[[1, 2, 3]] = "key is an array";
console.log(a[[1, 2, 3]]);  // -> "key is an array"

 取得できました。しかし、勘の良い方ならここで気づいていると思いますが、以下の場合はどうでしょうか。

const a = {};
a[[1, 2, 3]] = "key is an array";
console.log(a[[1, 2, "3"]]);  // -> "key is an array"

 取得できてしまいます。もうひとつ実験してみましょう。

const a = {};
a[[1, 2, 3]] = "key is an array";
console.log(a[[1, "2,3"]]);  // -> "key is an array"

 なんということでしょうか。これも想定した結果とは言い難いです。

Symbolを渡してみる

 しかし、そんな中でも特殊な動きを見せるSymbolというObjectがあります。これはコード上で常にユニークな振る舞いを見せるSymbolを生成します。

const symA = Symbol();
const symB = Symbol();

const a = {};
a[symA] = "key is a symbol";
console.log(a[symA]); // -> "key is a symbol"
console.log(a[symB]); // -> undefined

 SymbolはtoString()したとしてもSymbolにしかなりませんが、keyとしては固有の振る舞いとなっていることがわかります。

まとめ

 このように、ObjectのKeyにString以外のObjectを提供するのは危険が伴います。

 素直にstringを使うのが得策ですね。