オブジェクトを連想配列として使うと痛い目にあいますよ

2 min read読了の目安(約2200字 6

2億回くらい口をすっぱくして言ったような気がします。(嘘)

が、過去に何度も語られて語りつくされた話題なので、雑にまとめておきます。

JavaScript でオブジェクトを連想配列として使っちゃだめです。
だめ。全然だめ。絶対だめ。

こういう、『なんか工夫して使ったら使えちゃったから便利。』だけどあとからひどい目にあうという変な構文やテクニックがやたらおおくてカオスなのがJavaScriptの難しさです。気をつけましょう。

const inputValue = 'a';

const object1 = {
  a: 'Aです',
  b: 'Bです',
  c: 'Cです',
}

const isUndefined = value => typeof value === 'undefined';

if (isUndefined(object1[inputValue])) {
  console.log('処理がみつかりません');
} else {
  console.log(object1[inputValue]);
}

これで、inputValueがa,b,c,d など【さまざまな値】であっても、出力結果は次のどれか【のみ】なる。そのようにプログラムは読めますよね。

  Aです / Bです / Cです / 処理がみつかりません

ちがいます。全然ちがいます。絶対ちがいます。

inputValue に、'toString' や、 'constructor' を代入して調べてみてください。

  function toString() { [native code] }

とか

  function Object() { [native code] }

と出力されます。

object1 は、自分で定義してなくても、constructor や toString というプロパティを最初から持っているのです。

なので、このようなオブジェクトを連想配列扱いするようなプロパティに対する文字アクセスで分岐処理をするような使い方はしてはいけません。

そんな使い方は想定していないから大丈夫。

大丈夫なわけねーだろ。

という実例を教えていただいたので引用させていただきます。

https://twitter.com/kymn_/status/1297171791962546178

「以下では、バグの原因について報告します。 パフォーマンスをを計算する部分のコードについて、以下のようなコードがありました。(単純化しています。) rate = rates[user] ? rates[user] : default; ratesは内部レートを格納するJSONをパースしたObject型、userは参加者のIDです。」

ratesには「一度以上参加した参加者のレート」が格納されています。そのため、userが初参加ならばrates[user]は偽として評価され、rateにはdefaultが格納されるはずでした。
しかし、JavaScriptのオブジェクトはデフォルトで複数のメンバを持っています。例えば、"toString"や"constructor"です。

ratesには「一度以上参加した参加者のレート」が格納されています。そのため、userが初参加ならばrates[user]は偽として評価され、rateにはdefaultが格納されるはずでした。
しかし、JavaScriptのオブジェクトはデフォルトで複数のメンバを持っています。例えば、"toString"や"constructor"です。

ここで、このような名前を持つユーザーが参加した場合を考えます。
当然rates["constructor"]はundefinedではないため、真として評価されます。結果として、rateに数値でない値が入ってしまったためにその後の計算でNaNが発生し、連鎖的に様々なところが破壊されました。
これが今回起きたことです。

こわっ、、、こわすぎる。。。地獄や。

参考

それぞれリンク先のコメントまで十分に読みましょう。

https://qiita.com/impl_chamuji/items/277d225784e11a5a7937#comment-faa14506b0a83860c305

https://qiita.com/impl_s/items/3ab29783daf4f160bd1f#comment-54032c1bdb1f10e06c49

https://qiita.com/impl_s/items/3ab29783daf4f160bd1f#comment-c3827f4b3bd953d710c8

なるべくバグが発生しないプログラミングコードを書きましょう的な現場からは以上です。

追記

t12uさんから知識を増やしてくださる良いコメントを頂きましたのでコメント欄も読まれることをおすすめします。

念の為なのですが、この記事でいいたいのはキーと値のペアなデータ構造としてオブジェクトを使わないように、という事ではなく、特定文字列がトラブルの原因になる場合があるから、使い方に気をつけていきましょう、という意図で書いています。

より安全なプログラムを目指してがんばっていきましょう!