同値性とは、ふたつの値が等しいかどうかを判定するものです。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)  // true

Infinity

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))  // true

IDを比較する

あるオブジェクトが持つ値の同値性を再帰的に判定すれば、値が一致していることは確認できますが、複雑で大量のデータを持つオブジェクトの同値性をその方法で判定する方法は計算時間の都合から現実的でないこともあります。

そういうときには、オブジェクトが持つ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
    }
}

第3回 参照とコピー