Frontend interview practice question

How would you compare two objects in JavaScript?

HighIntermediateJavascript

Interview quick answer

Object comparison in JavaScript is a strategy decision: === checks identity, shallow checks fit flat data, and deep checks fit nested data. Learn when each approach is enough, where JSON.stringify breaks, and which edge cases need a real deep-equality helper.

Interview focus

This JavaScript interview question tests whether you can explain compare two objects in JavaScript: shallow vs deep vs JSON pitfalls, connect it to production trade-offs, and handle common follow-up questions.

  • compare two objects in JavaScript: shallow vs deep vs JSON pitfalls explanation without falling back to memorized definitions
  • Objects and Equality reasoning, edge cases, and production failure modes
  • How you would answer the most likely JavaScript interview follow-up
Practice more JavaScript interview questions
Interview answer drill

Use this JavaScript interview question to rehearse a quick answer, common mistake, follow-up, and production pitfall.

Full interview answer

How to compare two objects in JavaScript

Use === (or Object.is) when you mean the same object reference. For two separate objects, compare their own top-level keys and values when the data is flat, or use a tested deep-equality implementation when nested values must match. JSON.stringify(a) === JSON.stringify(b) is not a general deep-comparison solution.

Choose the cheapest correct strategy for the contract:

  • Identity comparison: do both variables point to the same object?
  • Shallow value comparison: do the relevant top-level keys and values match?
  • Deep value comparison: do supported nested objects and arrays match recursively?
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

Choose how to compare two objects based on intent and data shape.

Shallow object comparison in JavaScript

Use a shallow comparison for plain records when the relevant fields are own enumerable string keys and each value is primitive or intentionally compared by reference. The helper below compares top-level values with Object.is; it does not recurse into nested objects.

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;
}
                  

When shallow equality is enough

If a filter bar stores only flat primitive fields such as page, sort, and query, a shallow comparison is cheaper and easier to reason about than a recursive deep comparison.

JAVASCRIPT
const prevFilters = { page: 1, sort: 'price', query: 'gpu' };
const nextFilters = { page: 1, sort: 'price', query: 'gpu' };

console.log(shallowEqual(prevFilters, nextFilters)); // true
// Good enough for a refetch guard or memo check on flat primitive fields.
                  

Deep object comparison for nested values

Use a scoped recursive comparison when nested arrays and ordinary objects must match by value. The example below supports arrays, Date, RegExp, cycles, and shared-reference topology. It intentionally does not define equality for Map, Set, typed arrays, custom class instances, symbol keys, or non-enumerable properties; production code should make those rules explicit or use a library that matches its data contract.

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

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

  if (Object.getPrototypeOf(a) !== Object.getPrototypeOf(b)) return false;

  if (a instanceof Date) return Object.is(a.getTime(), b.getTime());
  if (a instanceof RegExp) {
    return a.source === b.source && a.flags === b.flags && a.lastIndex === b.lastIndex;
  }

  const aIsArray = Array.isArray(a);
  const bIsArray = Array.isArray(b);
  if (aIsArray !== bIsArray) return false;

  if (!aIsArray) {
    const prototype = Object.getPrototypeOf(a);
    if (prototype !== Object.prototype && prototype !== null) return false;
  }

  if (seenA.has(a) || seenB.has(b)) {
    return seenA.get(a) === b && seenB.get(b) === a;
  }
  seenA.set(a, b);
  seenB.set(b, a);

  if (aIsArray && a.length !== b.length) return false;

  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], seenA, seenB)) 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)
                  

Can you compare JavaScript objects with JSON.stringify?

Only for a deliberately narrow JSON-data contract. The shortcut can produce the wrong result because:

  • Equal key/value sets can serialize differently when their insertion order differs.
  • Object properties whose values are undefined, functions, or symbols are omitted.
  • NaN and infinities serialize as null, while BigInt and circular references throw.
  • Date becomes a string, and Map, Set, class instances, or custom toJSON() methods need explicit semantics.
JAVASCRIPT
const q1 = { sort: 'price', page: 1 };
const q2 = { page: 1, sort: 'price' };
console.log(JSON.stringify(q1) === JSON.stringify(q2)); // false

const p = { a: 1, b: undefined };
const q = { a: 1 };
console.log(JSON.stringify(p) === JSON.stringify(q)); // true
                  

Object comparison edge cases

  • Dates usually need value comparison such as getTime().
  • Functions usually compare by identity, not by source text.
  • Cycles and repeated references require consistent pair tracking, not just a visited-object set.
  • Maps, sets, typed arrays, prototypes, property descriptors, symbol keys, and non-enumerable properties need an explicit equality contract.

Which object comparison strategy should you use?

  • Use === for object identity checks; it is cheap and exact.
  • Use shallow comparison for flat records and render optimizations where nested identity is intentional.
  • Use deep comparison only after defining supported types and whether repeated-reference topology matters. Choose a proven library whose contract matches those requirements; for example, fast-deep-equal is useful when cyclic graphs are out of scope.
  • Test the values your contract supports, including NaN, dates, arrays, functions, cycles, and repeated references.

Example: comparing filter objects

A product filter panel compares previous and next query objects to decide whether to refetch results. A reference check treats separately created but equal-by-value objects as different, while an unconditional deep comparison can add work on every keystroke. For flat filter state, stable object creation plus a targeted shallow comparison is usually the clearer contract.

Common pitfalls

  • Using === when the requirement is value equality.
  • Using JSON.stringify where insertion order, omitted values, special types, or cycles matter.
  • Deep-comparing large objects in hot render paths without measuring the cost.

Practice the decision

Implement recursive comparison in the JavaScript deep-equality challenge, distinguish equality from copying with shallow versus deep copy, or apply state and data-shape decisions in frontend machine-coding practice.

Guides
Preparing for interviews?