JavaScriptの値はオブジェクトかオブジェクト以外かの2つ
JavaScript初心者が一回はつまづくであろうオブジェクトの参照保持についてまとめ。
参考資料/出典はこちら
基本型値とオブジェクト値
まず第一に、JavaScript(TypeScript)の値は2種類に分けられる。
- (undefinedやnull、boolean、number、stringといった)基本型値
- (配列や関数を含む)オブジェクト値
要するに、JavaScriptの値(の持ち方)はオブジェクトかオブジェクト以外かということになる。
基本型値
基本型の値は不変であり、比較する時は値を用いて行う。シンプルに、2つの基本型値が同じ値を持つ場合のみ2つの値は等しいと判定される。
例えば、2つの異なる文字列を比較する場合、両者が同じ長さかつ各インデックスの文字が同じだと、2つの文字列値は等しいと判定される。
const str1 = "hello";
const str2 = "hello";
str1 === str2; // => true
これは初心者でも簡単に理解できる。
オブジェクト値
オブジェクトの値は可変であり、比較する時も値を用いて行わない。同じプロパティと値を持つ2つのオブジェクトがあっても、これらは等しいとは判定されない。同様に、同じ要素を同じ順序で持つ配列も等しいとは判定されない。
つまり、2つのオブジェクトは両方が同じオブジェクトを参照している場合のみ同一と判定される。
このようなオブジェクトの値の持ち方は、基本型と区別するために参照型と呼ばれることもある。
// 中身が同じでも同一ではない
let obj1 = { a: 1 };
let obj2 = { a: 1 };
let ary1 = [];
let ary2 = [];
obj1 === obj2 // => false
ary1 === ary2; // => false
// 参照が同じだと同一と判定される
let ary3 = [];
let ary4 = ary3; // ary4とary3は同じ配列を参照する
ary4[0] = 1; // ary4を使って配列を更新
ary3[0] // => 1: ary3からも変更内容は確認できる
ary3 === ary4 // => true
変数にオブジェクトを代入することは単に参照を代入しているだけで、オブジェクトの新しいコピーを作成するわけではない。
オブジェクトや配列を複製したい場合は、forループなどを使ってオブジェクトのプロパティや配列の要素を明示的にコピーしなければならない。
現実的には、配列だったらArray.from()
、オブジェクトであればObject.assign()
やスプレッド演算子を使って複製する。
オブジェクトを比較するには
前述の通り、オブジェクトは参照で比較を行うため、中身での比較は基本的にできない。
どうしても比較したい場合、次の方法がある。
const obj1 = ["tokyo", "oska", "nagoya"];
const obj2 = ["tokyo", "oska", "nagoya"];
JSON.stringify(obj1) === JSON.stringify(obj2) // => true
オブジェクトは比較できないので、文字列(基本型値)に変換して比較している。ただし、オブジェクト(配列)の中身の順番が違っていると正しく比較できない。配列はそれでもいいかもだが、オブジェクトのプロパティの順番は意味がないので、比較方法としては微妙。
それでもどうしてもオブジェクトを比較したい場合、lodash
またはunderscore
ライブラリを使用するとできるらしい。
const object1 = { name: sato, age: 28 };
const object2 = { name: sato, age: 28 };
const isEqualObj = _.isEqual(object1, object2);
console.log(isEqualCars); // => true
オブジェクトを比較するためにライブラリ入れるのもなーという印象。
結局のところ、オブジェクトを比較しなくてもいいような設計した方が良さそう。(そりゃそうだ)
Discussion