How Lodash isEqual Performs Deep Comparison

Lodash's _.isEqual method provides deep value comparison across JavaScript data structures, moving beyond the reference-based checks of the native strict equality operator (===). To determine whether two values are functionally and structurally identical, the utility combines primitive fast-paths, internal type-tag checks, specialized object unwrapping, recursive traversal, and cycle detection. This article breaks down the internal algorithms and heuristics Lodash utilizes to implement robust deep equality.

1. The Fast-Path Primitive Comparison

Before initiating expensive traversal algorithms, _.isEqual performs a rapid equality check. It relies on the SameValueZero algorithm, which mirrors === with two exceptions: it treats NaN as equal to NaN, and +0 as equal to -0. If both references are identical or both values evaluate to true under this primitive check, the function immediately returns true.

2. Internal Tag Resolution

When values fail the initial fast-path check, Lodash determines their underlying JavaScript types using internal type tags obtained via Object.prototype.toString.call(). This step ensures that values sharing an apparent structural similarity but differing in underlying specifications (for instance, an Array versus an Arguments object) are identified correctly before comparing values. If the type tags differ, the comparison immediately returns false.

3. Specialized Value Unwrapping

Lodash defines explicit comparison routines for built-in JavaScript objects based on their internal tag:

4. Collection Handling: Sets and Maps

For modern ES6 collections like Map and Set, Lodash first checks the size property as a quick-fail condition. If sizes match:

5. Object and Array Traversal

For plain objects and arrays, Lodash executes a structured recursive traversal:

6. Circular Reference and Cycle Detection

To prevent infinite recursion when traversing self-referencing or circularly-linked data structures, Lodash maintains internal tracking stacks (historically parallel arrays, modernly implemented via tracking structures like Map or Set). Before evaluating nested properties, the algorithm checks if the current pair of objects is already undergoing comparison in the active call stack. If the pair has already been traversed, Lodash treats the reference as equal and unwinds the recursion.