SameValueZero Equality in JavaScript
SameValueZero is a JavaScript equality algorithm that treats `NaN` as equal to `NaN` and treats `+0` and `-0` as equal. It is used by operations such as `Set`, `Map` key matching, and `Array.prototype.includes()`.
SameValueZero is one of JavaScript's internal equality algorithms. Unlike strict equality (`===`), it considers `NaN` equal to itself. This matters because `NaN === NaN` is `false`, while SameValueZero matching can successfully find a `NaN` value.
SameValueZero also treats `+0` and `-0` as equal. This matches the behavior of `===`, even though `Object.is(+0, -0)` distinguishes the two signed zeros.
You normally encounter SameValueZero indirectly rather than calling it yourself. `Array.prototype.includes()` uses it to search arrays, and `Set` and `Map` use the same matching semantics for values and keys. This makes `NaN` behave naturally as a stored or searchable value in these APIs.
The distinction is important when choosing an equality operation. Use `===` for ordinary strict comparisons, `Object.is()` when the distinctions around `NaN` and signed zero matter, and remember SameValueZero when reasoning about collection membership and key matching.
Key points
- `NaN` matches `NaN` under SameValueZero.
- `+0` and `-0` are considered equal.
- `includes()`, `Set`, and `Map` use SameValueZero-style matching.
- SameValueZero differs from `===` primarily for `NaN`, and from `Object.is()` for signed zero.
Interview questions
What is SameValueZero in JavaScript?
SameValueZero is an equality algorithm used by several JavaScript collection and search operations. It treats `NaN` as equal to itself, unlike `===`. It also considers `+0` and `-0` equal.
Why does `[NaN].includes(NaN)` return `true` while `[NaN].indexOf(NaN)` returns `-1`?
`includes()` uses SameValueZero for element matching, so `NaN` matches another `NaN`. `indexOf()` uses strict-equality-style comparison, where `NaN` does not equal itself. Therefore `includes()` returns `true`, while `indexOf()` fails to find the element and returns `-1`.
How does SameValueZero affect `Set` and `Map`?
`Set` uses SameValueZero-style value matching when determining whether an element already exists. `Map` uses the same matching semantics for keys. Consequently, a `NaN` key can be retrieved using another `NaN`, and `+0` and `-0` identify the same key.
Compare `===`, `Object.is()`, and SameValueZero.
All three are distinct equality algorithms with different edge-case behavior. `===` does not consider `NaN` equal to itself and considers the signed zeros equal. `Object.is()` considers `NaN` equal to itself but distinguishes `+0` and `-0`. SameValueZero matches `NaN` with itself and treats the signed zeros as equal.
Can you demonstrate the practical difference between `includes()` and `indexOf()`?
Yes. For an ordinary value such as `2`, both can locate the element. For `NaN`, `includes(NaN)` returns `true` because SameValueZero considers the values equal. `indexOf(NaN)` returns `-1` because its comparison does not treat `NaN` as equal to itself.