jsのオブジェクトについて理解を深める

2021.5.14

会社の上司に面白いことを教えて頂いたので、改めて自分の頭の整理を兼ねてまとめようと思います。

今回のテーマ

{}==={}は、falseらしい。
え、ほんと?
いやいや、どう見てもtrueでしょ!
そう疑っていた私ですが、コンソールで実際に実行し、確かめたところをfalseが返され、驚きを隠すことができません。
もし、ここまで読み、それでも信じることができないという方は、手元で下記のコードを実行し、確認してみてください。

console.log({} === {}); // false

falseになる理由

早速ですが、falseになる理由について解説していこうと思います。
JavaScriptにおけるオブジェクトや配列は、プリミティブ値ではなく、メモリ内の値として扱われます。
つまり、生成されたそれぞれのインスタンスに対してメモリ内の番地が付与されるイメージです。
オブジェクトAに対しては0という番地、オブジェクトBに対しては1という番地が与えられたとします。
このとき、オブジェクトAとオブジェクトBを比較しようとすると参照する番地が0と1で異なるため、{} === {}はfalseになるということです。

ここまで解説したところで、最初のコードに戻ってみましょう。
このコードは空のオブジェクトを比較したいだけなのに、参照しているメモリ内の番地が異なるため、falseが返ってきてしまいました。
それでは、上記のような比較をしたい場合はどうしたらいいのか?
場合にもよりますがプリミティブ値で比較してあげるのが簡単かと思います。

console.log(Object.keys({}).length === Object.keys({}).length); // true

上記のコードでは、オブジェクトの参照元で比較するのではなく、オブジェクトのlength、つまり返されるNumber型のプリミティブ値で比較されるのでtrueが返されるという訳です。

実践編

{}==={}は、falseだ!で終わるのであればどれだけ幸せだったことでしょうか。
勘がいい方は、既に{}==={}がfalseになるということの恐ろしさに気づいていることでしょう。

この話で重要なことは、オブジェクトはメモリの値を参照するという部分でした。
そのことを頭に入れて下記のコードを見てみましょう。

var obj = { name: "foo" };
var a = obj;
a.name = "bar";
console.log(a.name); // bar
console.log(obj.name); // bar

上記のコードですが、一見すると最後の行の obj.nameはfooが出力されるように思われます。
しかし、実際に出力されるのはbarです。
理由は、上記で述べてきた通りではあるのですが、変数aにobjを格納したことで少しややこしくなってしまいましたね。 要は、変数aも変数objも参照するメモリの値は同じだということです。
注意点としは、参照元であるobjという変数の値が書き換わったという点です。
つまり、オブジェクトの値を変更する場合は参照元のオブジェクトの値が変わることも想定していないと意図しないバグなどを生む原因となることです。

まとめ

  • オブジェクトや配列は、メモリの値を参照する
  • 比較するときはプリミティブ値に変換する

オブジェクトの値を書き換える時には、参照元の値が変わることを意識していきたいですね。
JavaScriptの言語仕様理解に一歩近づいた気がします。