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を使うのが得策ですね。