$ snippet deep-equal

Deep equality check

Compare two values structurally — nested objects, arrays, Dates, Maps, Sets, NaN — the way you wish === worked.

JavaScript
function deepEqual(a, b) {
  if (Object.is(a, b)) return true;                       // handles NaN, avoids +0/-0 confusion
  if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) return false;
  if (Object.getPrototypeOf(a) !== Object.getPrototypeOf(b)) return false;

  if (a instanceof Date) return a.getTime() === b.getTime();
  if (a instanceof RegExp) return a.source === b.source && a.flags === b.flags;
  if (a instanceof Map) {
    if (a.size !== b.size) return false;
    for (const [k, v] of a) if (!b.has(k) || !deepEqual(v, b.get(k))) return false;
    return true;
  }
  if (a instanceof Set) {
    if (a.size !== b.size) return false;
    for (const v of a) if (!b.has(v)) return false;       // Set members compared by identity
    return true;
  }
  if (ArrayBuffer.isView(a)) {
    if (a.length !== b.length) return false;
    for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
    return true;
  }

  const keysA = Reflect.ownKeys(a), keysB = Reflect.ownKeys(b);
  if (keysA.length !== keysB.length) return false;
  for (const k of keysA) {
    if (!Object.prototype.hasOwnProperty.call(b, k)) return false;
    if (!deepEqual(a[k], b[k])) return false;
  }
  return true;
}

How it works

The JSON.stringify(a) === JSON.stringify(b) trick fails on key order, undefined, Dates vs strings, and anything JSON can't represent. This version compares primitives with Object.is, special-cases the built-in container types, then compares own keys recursively — arrays fall out of the same logic because their indices are own keys and their length differs if sizes differ.

Gotchas

Try it
Result
Ad slot