Comparisons S1
- Stage: Stage 1
- Status: Active
- ECMAScript edition: —
- Synchronized: Aug 28, 2026
- 中文译文 · Source repository
This proposal introduces a standardized deep comparison API to address the lack of built-in support for determining equality and differences between values, especially objects. It provides a compare function that can return either a boolean (fast mode) or an iterator of deviations (full mode), with configurable options for handling edge cases like constructors, prototypes, and descriptors. The proposal is foundational and intended to support future sibling proposals like Inspector and Modes.
The README below comes from the upstream repository and may contain outdated stage or status metadata. Use the proposal details above as the current source of truth.
Proposal: Comparisons
Champions:
- Jacob Smith (@JakobJingleheimer)
- Richard Gibson (@gibson042)
Authors:
- Jacob Smith (@JakobJingleheimer)
- Ruben Bridgewater (@BridgeAR)
Stage
Current: 1
The Problem
Determine whether and/or how A and B deviate from each other—a very common need that is currently solved only for very narrow cases (primitives, and to some extent JSON.stringifyable data structures). This issue has 2 parts: (deep) equality and details.
Motivation
Facilitate making decisions about deep equality that users are often unaware of.
Walking an object is difficult and not fun; determining equality can be difficult, requiring an enormous amount of specific knowledge that the vast majority of users don’t have. These complexities create significant barriers and risks to users.
Equality (currently)
Primitives are mostly trivial: Object.is provides the strictest comparison (SameValue), and === (IsStrictlyEqual) is only slightly looser, failing to differentiate oppositely-signed zeros and failing to equate NaNs.
But what "similar" means is not straightforward for objects. A human considers these "equal" (but the language does not):
There is some variation in the ecosystem regarding the nuances of comparing objects.
Even more important is the details: Merely knowing that A and B differ is almost useless without knowing specifically how they differ.
Annoying:
Better
But brittle
Use-cases
Production: Delta for HTTP PATCH
Many client-side apps manipulate data, sometimes very large data. That could be via a <form>, a text editor, or something else. Since the before and after are known, only the delta is needed (sent via http patch).
Production: Logging
Production: State management
In frameworks such as React, state is often based on derived data in which updates don't always include a material change:
This is currently left up to the user to guard against because it's too difficult and expensive to for the library to check.
React tried to get this before (see Prior Art → Shallow Equal).
Production: Validation
Input from an uncontrolled origin:
Production: Virtual DOM
Testing
Explicitly out of scope
- This is not a test runner (
describe,it, etc). - This is not a test utility suite (
mock,stub, etc).
Solution (sketches)
Compare
A function to deeply compare values.
CompareOptions
- mode
- How the comparison reports the result
- mode
fast - Return
truewhen deviation(s) exist orundefinedwhen no deviation exist. - mode
full - Return an
IteratorofDeviationswith all deviations, orundefinedwhen no deviation exist. - reasons
- Whether/how to handle more esoteric cases when determining differences.
- reasons.constructor
false|true - Whether to consider constructor. This affects, amongst others, Box Primitives (
new Boolean(true)vstrue) and TypedArrays (new Int8Array([1,2])vsnew Uint8Array([1,2])) where differenes are pedantic. - reasons.descriptors
false|true - Whether to consider property non-enumerability descriptors (configurable, getter vs value, writeable).
- reasons.promise
'ref'|'value' - How to determine equality of promises.
- reasons.prototype
false|true - Whether to consider prototype.
- reasons.weak
'ref'|'value' - How to determine equality of Weak objects (
WeakMap,WeakRef,WeakSet).
Deviations
- path
- An array containing an ordered list (where the first is the outmost and the last is the current) of keys representing the path to the deviation (
['foo', 1, Symbol('zed')]). When comparing non-objects, (eg strings), path is an empty array ([]). - actual
- The leaf value from the second argument.
- expected
- The leaf value from the first argument.
- reason
-
The reason(s) comparison failed to match.
Reason(s) are general to specific, outter-most to inner-most:
compare(true, new Date())→ "type" is the reason for the deviation. Specific order is engine-defined.
Equality
To avoid suppressing potentially relevant differences, primitive values are compared with SameValue (which does not differentiate any NaN but does differentiate -0 from 0/+0). This might become configurable via CompareOptions.
- TypedArrays containing the same values in the same sequence are equal, except when
CompareOptions.reasons.constructoris enabled. - A box primitive (eg
new Boolean(true)) equals its primitive (egtrue), except whenCompareOptions.reasons.constructoris enabled.
Custom types are handled by HostTypes (to avoid custom comparison).
Examples
Fast mode: equal
Fast mode: unequal
Full mode: unequal
Fast mode: object descriptor vs literal
Full mode: type unequal
Full mode: non-enumerable value
Full mode: non-enumerable getter
Full mode: multiple leafs unequal, plus red-herring from type-mismatch
Full mode: multiple leafs unequal and missing
Full mode: multiple leafs unequal and prototype
Full mode: multiple array items unequal and missing
Sibling proposals
The current proposal is useful on its own and sets a foundation for the following to be addressed subsequently.
The current proposal does not include features likely to attract customisation, so punting these delays the need to determine how customisation will be facilitated.
Other related proposals
Prior art
Assertions and expectations
The vast majority of ECMAScript engineers use one of 2 forms: assert and expect. These come from one of ~4 libraries: chai (20M weekly), jasmine (1.4M weekly), jest (29M weekly), node:assert (indeterminable). These are direct competitors, so we can assume there is no overlap and the numbers are summable: at least ~51M weekly (probably significantly higher when node:assert numbers are added).
Assert
node:assertandchai's TDD set have large overlap.
Expect
jasmineandjestare (nearly?) identical with dedicated methods:expect(a).toEqual(b)chai's BDD set is a chain-style that builds upon itself:expect(a).to.equal(b)
Neighbours
Many major languages natively include a form of assertion. To name a relevant few: