How Lodash isEqual Handles Object Key Ordering
This article examines how Lodash’s _.isEqual method
performs deep equality comparisons on JavaScript objects, specifically
regarding the sequence of object keys. You will learn about the internal
mechanism used by Lodash—primarily the baseIsEqualDeep and
equalObjects functions—and why key ordering does not affect
the outcome of an equality check.
The Core Algorithm:
equalObjects
Lodash delegates deep comparison tasks to an internal function named
baseIsEqual, which routes object comparisons through
baseIsEqualDeep and ultimately to
equalObjects.
When comparing two plain objects, the algorithm follows these specific steps:
- Key Extraction: The algorithm retrieves the
enumerable own property names of both objects using an internal
implementation equivalent to
Object.keys(). - Length Verification: It compares the total number
of keys. If the two objects do not have the same number of keys, the
comparison immediately returns
false. - Key Existence and Value Recursion: The algorithm
iterates through the keys of the first object. For each key, it checks
whether the second object also possesses that key using
hasOwnProperty. If the key exists on both objects, it recursively callsbaseIsEqualto compare the corresponding values. - Circular Reference Handling: A tracking stack (via
Lodash's internal
Stackcache) is used during recursion to detect and safely resolve circular references.
Why Key Ordering Does Not Affect Equality
Even though ECMAScript 2015 (ES6) introduced a deterministic iteration order for object keys in JavaScript, standard objects are fundamentally treated as unordered collections of key-value pairs in computer science and data modeling.
Lodash’s equalObjects algorithm does not compare key
lists by index. Instead of asserting that
keysA[i] === keysB[i], it verifies that every key present
in objectA also exists as an own property in
objectB with an equivalent value.
Consider the following example:
const objectA = { a: 1, b: 2 };
const objectB = { b: 2, a: 1 };
_.isEqual(objectA, objectB); // returns trueDuring execution:
objectAyields keys['a', 'b'].objectByields keys['b', 'a'].- Both key arrays have a length of
2. - Lodash checks
objectA's keys againstobjectB:- Does
objectBhave property'a'? Yes, and1 === 1. - Does
objectBhave property'b'? Yes, and2 === 2.
- Does
- All checks pass, and
_.isEqualreturnstrue.
Comparison with Ordered Structures
The algorithm intentionally differentiates between unordered key-value collections (like plain objects) and ordered collections:
- Arrays and TypedArrays: Evaluated using
equalArrays, which strictly enforces index-based ordering (arrayA[0]is compared toarrayB[0]). - Maps: Evaluated using entry matching similar to
objects, meaning insertion order inside
Mapinstances is also ignored unless custom comparators are provided. - Sets: Evaluated by checking value presence regardless of insertion sequence.
By focusing on property membership and recursive value equality rather than iteration order, Lodash ensures that objects representing identical states evaluate as equal, regardless of how or when their properties were defined.