How Lodash Distinguishes Array-Like Objects
In the Lodash JavaScript library, _.forEach determines
how to iterate over an item by evaluating structural characteristics
rather than inspecting constructor names or class prototypes. Through an
internal duck-typing check called isArrayLike, Lodash
checks for the existence of a valid numeric length
property; if present and valid, it runs an index-based loop
(0 to length - 1), and if absent, it falls
back to iterating over the object's enumerable keys.
The Internal Mechanism:
isArrayLike
When _.forEach (or its internal implementation
baseEach) receives a collection, it branches execution
based on the isArrayLike utility function. The check
follows this logic:
function isArrayLike(value) {
return value != null && typeof value !== 'function' && isLength(value.length);
}This single evaluation distinguishes standard arrays, strings, DOM
NodeLists, and the arguments object from
standard objects and class instances.
The Role of isLength
For an object to qualify as array-like, its length
property must pass the isLength validator:
const MAX_SAFE_INTEGER = 9007199254740991;
function isLength(value) {
return typeof value === 'number' &&
value > -1 &&
value % 1 === 0 &&
value <= MAX_SAFE_INTEGER;
}The property must be a non-negative, safe integer.
Handling Standard Class Instances
A standard class instance or object in JavaScript typically does not
define a length property. When passed to
_.forEach:
value.lengthevaluates toundefined.isLength(undefined)returnsfalse.isArrayLike(instance)resolves tofalse.
Because the class instance fails the array-like check, Lodash treats
it as a standard key-value dictionary. It routes the instance to
baseForOwn, which retrieves all own enumerable string and
symbol keys via Object.keys (or Lodash’s internal
keys utility) and invokes the iteratee for each
property.
Prototype and Function Guards
Lodash includes explicit guards to prevent false positives:
- Functions: Functions have a built-in
lengthproperty representing their parameter count. Thetypeof value !== 'function'check prevents functions and class constructors from accidentally being treated as array-like collections. - Structural Duck Typing: Lodash avoids using
instanceofor checkingObject.prototype.toString. As a consequence, if a custom class instance explicitly defines a numericlengthproperty (such as a custom collection class),isArrayLikeevaluates totrue. In that scenario,_.forEachbypasses named properties and iterates through integer indices from0tolength - 1.