Javascriptの等価性について

Javascriptのinclude()の引数とか使い方を調べてたら、 MDNのページ(include()) に

与えられた要素が見つかるかどうかを計算するために、 SameValueZero (ゼロの同値) アルゴリズムを使用します。

と、ありましたがSameValueZeroってなんだろうとなったので調べてみたところ、 Javascriptの比較について少し理解が深まったので記事に残しておきます。

比較について

まず、Javascriptの比較では4種類の比較がある。

以下はMDNからまとめます。

  • 抽象的な等価性比較(Abstract Equality Comparison)

    • ==
  • 厳格な等価性比較(Strict Equality Comparison)

    • ===
    • Array.prototype.indexOf, Array.prototype.lastIndexOf, case の一致で使用
  • ゼロの同値(SameValueZero)

    • %TypedArray%ArrayBuffer コンストラクターMapSet の操作、 ES2016 で追加された String.prototype.includes で使用されます
  • 同値(SameValue)

    • 上記以外のすべての状況で使用されます

今までコードを書くのに上の2つくらいしか知らなかった。。。

イコール2つ(等価演算子)は緩い比較を行う為、意図しない挙動をすることで有名ですね。

今回はSameValueZeroSameValueに絞って、どんな比較か見てます。

SameValue

SameValueから見ていくと分かりやすかったですが、 SameValueObject.isメソッドによって提供されています。

Object.is() は 2 つの値が同一値を判定します。2 つの値は以下の規則の一つに当てはまる場合同一となります。

  • どちらもundefined
  • どちらもnull
  • どちらも true かどちらも false
  • どちらも同じ文字からなる同じ長さの文字列
  • どちらも同じオブジェクト
  • どちらも数で、

    • どちらも +0
    • どちらも -0
    • どちらもNaN
    • あるいはどちらもゼロ以外でNaNでなく、同じ数値を持つ

パッと見るととても厳格な比較をしてくれそう。

試してみる。

Object.is('foo', 'foo');     // true
Object.is(window, window);   // true

Object.is('foo', 'bar');     // false
Object.is([], []);           // false

var foo = { a: 1 };
var bar = { a: 1 };
Object.is(foo, foo);         // true
Object.is(foo, bar);         // false

Object.is(null, null);       // true

// 特殊なケース
Object.is(0, -0);            // false
Object.is(-0, -0);           // true
Object.is(NaN, 0/0);         // true

厳密等価演算子との違いは、

  • NaN === NaNがfalseになるところで、Object.is(Nan, NaN)ではtrueになる。
  • 0 === -0がtrueになるところObject.is(+0, -0)では falseとなる。

SameValueZero

SameValueZeroについてMDNはこう書いています。

Same-value 等価性に似ていますが、 +0 と -0 は等しいとみなします。

以上。SameValueを先に見たからわかりやすい。

includes()では、このSameValueZeroによる比較を行っているようです。

感想

比較はプログラムで使用頻度の高い重要な構文ですが、 比較する対象の見積もりが甘いとあっという間にバグとなります。

今回調べたObject.is()なんかは比較する結果が分かりやすいので、 これから使っていきたいですね。 ただ、IE11以下は使えないので要Polyfillのようです。

参照