How would you compare two objects in JavaScript?

HighIntermediateJavascript
Quick Answer

In JavaScript, object comparison depends on intent: === checks reference identity, while value-based checks require shallow or deep comparison. Learn practical comparison strategies, JSON.stringify pitfalls, and when to use utility libraries for reliable deep equality.

Answer

The core idea

Object comparison in JavaScript is not one single operation. You usually choose between three intents:

  • Identity comparison: are both variables pointing to the exact same object?
  • Shallow value comparison: are top-level keys and values equal?
  • Deep value comparison: are nested objects/arrays equal by content?

Most interview mistakes happen when people use === but expect deep value equality.

JAVASCRIPT
const a = { id: 1, profile: { city: 'Berlin' } };
const b = { id: 1, profile: { city: 'Berlin' } };
const c = a;

console.log(a === b); // false (different references)
console.log(a === c); // true  (same reference)
                  

Approach

What it checks

Best use case

Main risk

===

Reference identity only

React memoization, state identity checks

Returns false for equal-looking but separate objects

Shallow compare

Top-level keys + values

Flat config objects, props optimization

Misses differences in nested objects

Deep compare

Recursive structural equality

Validation, tests, cache keys, diffing

Higher CPU cost and edge-case complexity

Pick comparison strategy based on intent and data shape.

Shallow comparison helper

Use shallow compare when object values are primitives or when nested objects are intentionally compared by reference.

JAVASCRIPT
function shallowEqual(obj1, obj2) {
  if (obj1 === obj2) return true;
  if (!obj1 || !obj2 || typeof obj1 !== 'object' || typeof obj2 !== 'object') return false;

  const keys1 = Object.keys(obj1);
  const keys2 = Object.keys(obj2);
  if (keys1.length !== keys2.length) return false;

  for (const key of keys1) {
    if (!Object.prototype.hasOwnProperty.call(obj2, key)) return false;
    if (!Object.is(obj1[key], obj2[key])) return false;
  }

  return true;
}
                  

Deep comparison helper (handles cycles)

For nested data, deep comparison is safer. A robust helper should handle arrays, dates, and circular references.

JAVASCRIPT
function deepEqual(a, b, seen = new WeakMap()) {
  if (Object.is(a, b)) return true;

  if (typeof a !== 'object' || a === null || typeof b !== 'object' || b === null) {
    return false;
  }

  if (a.constructor !== b.constructor) return false;

  if (a instanceof Date) return a.getTime() === b.getTime();
  if (a instanceof RegExp) return String(a) === String(b);

  if (seen.get(a) === b) return true;
  seen.set(a, b);

  if (Array.isArray(a)) {
    if (!Array.isArray(b) || a.length !== b.length) return false;
    for (let i = 0; i < a.length; i++) {
      if (!deepEqual(a[i], b[i], seen)) return false;
    }
    return true;
  }

  const keysA = Object.keys(a);
  const keysB = Object.keys(b);
  if (keysA.length !== keysB.length) return false;

  for (const key of keysA) {
    if (!Object.prototype.hasOwnProperty.call(b, key)) return false;
    if (!deepEqual(a[key], b[key], seen)) return false;
  }

  return true;
}
                  
JAVASCRIPT
const x = { id: 1, meta: { tags: ['js', 'interview'] } };
const y = { id: 1, meta: { tags: ['js', 'interview'] } };

console.log(shallowEqual(x, y)); // false (nested object references differ)
console.log(deepEqual(x, y));    // true  (same nested values)
                  

Why JSON.stringify is not a universal solution

The stringify trick is fast to write, but it breaks in real systems:

  • Key order can differ between objects with same logical meaning.
  • undefined, functions, and symbols are dropped.
  • Circular references throw errors.
  • Date, Map, Set, and class instances need special handling.
JAVASCRIPT
const p = { a: 1, b: undefined };
const q = { a: 1 };

console.log(JSON.stringify(p) === JSON.stringify(q));
// true (but objects are not semantically identical for many use cases)
                  

Production guidance

  • Use === for identity checks (cheap and exact).
  • Use shallow compare for flat objects and render optimization.
  • Use a proven library (for example fast-deep-equal) when deep equality must be reliable and fast.
  • In tests, include edge cases: NaN, dates, arrays, and circular references.

Practical scenario
A product filter panel compares previous and next query objects to decide whether to refetch results. A strict reference check misses equal-by-value objects, while naive deep checks hurt performance on every keystroke.

Common pitfalls

      • Using === when your intent is value equality.
      • Using JSON.stringify for complex or cyclic objects.
      • Running deep comparison on large objects inside hot render loops.
Trade-off or test tip
Choose comparison depth by use case, then benchmark. In UI code, prefer stable object creation plus targeted shallow checks; in validation/tests, use robust deep equality.

Similar questions
Guides
Preparing for interviews?

Use the relevant interview-question hub first, then move into a concrete study plan before targeted company sets.