同値性とは、ふたつの値が等しいかどうかを判定するものです。JavaScriptにおいて、Boolean型、Number型(一部除く)、String型は ==または === 演算子による比較ができます。
これらの値は数学的に同値であることを示す3つの規則を満たします。
- 反射律:
x == x - 推移律:
x == y && y == zならばx == z - 対象律:
x == yならばy == x
例外や特殊な値
NaN
NaNはNumber型ですが、反射律を満たしません。この性質から == による判定が簡単にはできないので、NaNの判定には Number.isNaN()を利用するようにしてください。
console.log(typeof NaN == 'number') // true
console.log(NaN == NaN) // false
console.log(NaN != NaN) // trueInfinity
Infinityと-Infinityは反射律を満たしますが、ほとんどの数値計算に対してInfinityを返すという面白い性質を持ちます。そのため、Number型に0以外の値を足し合わせたら値が変わるという期待をしないでください。
console.log(Infinity + 1 == Infinity) // true実数の丸め誤差
実数の計算では一見正しそうな式が一致しないこともあります。
console.log(2.4 * 3 == 7.2) // falseこれを丸め誤差と呼び、コンピュータの実数表現力に限界があるために発生するもので、組込みのNumber型では避けられません。128bit実数型や固定小数点型、分数型を導入することで改善できることがあります。
オブジェクト型の同値判定
nullを除くオブジェクト型つまり、typeof x == 'object' を満たす値は、==で 比較しても、メモリアドレスが一致していることの判定になり、同値性を判定することはできません。
const a = {}
const b = {}
console.log(a == a) // true
console.log(a == b) // false
console.log(b == b) // trueすべての値の同値を確認する
オブジェクト型が正しいことは、オブジェクトの型が一致していることを確認した上で、オブジェクトが持つ値がすべて一致することで確認できます。
例えば以下のUserクラスは名前と年齢を持ちますが、それらの同値性を確認することになります。JavaScriptの言語的に決められた方法はないので、ここでは比較を簡単に行うための equals() メソッドを定義して判定できるようにしています。
class User {
constructor(name, age) {
this.name = name
this.age = age
}
equals(other) {
if (other instanceof User) {
return this.name == other.name
&& this.age == other.age
}
return false
}
}
const a = new User('Alice', 14)
const b = new User('Alice', 14)
console.log(a.equals(b)) // trueIDを比較する
あるオブジェクトが持つ値の同値性を再帰的に判定すれば、値が一致していることは確認できますが、複雑で大量のデータを持つオブジェクトの同値性をその方法で判定する方法は計算時間の都合から現実的でないこともあります。
そういうときには、オブジェクトが持つIDを比較することでこれを省略する方法が執れるでしょう。ただし、IDは一意なものでなければなりません。
class User {
constructor(userId, name, age) {
this.userId = userId
this.name = name
this.age = age
}
equals(other) {
if (other instanceof User) {
return this.userId == other.userId
}
return false
}
}