How Lodash _.max Bypasses Array Prototype Properties
Lodash's _.max bypasses prototype properties when
processing large numeric arrays by utilizing index-based iteration
constrained by the array's length property rather than
object key enumeration. Instead of traversing object keys using
mechanisms that climb the prototype chain, Lodash delegates the
operation to internal base methods that access elements via sequential
integer indexes. This approach ensures maximum execution speed on
massive datasets, prevents inherited properties from polluting
calculations, and enables JavaScript engines to optimize memory access
patterns.
Index-Based Traversal
via baseExtremum
Under the hood, _.max delegates evaluation to an
internal function named baseExtremum. Rather than using a
for...in loop—which enumerates all enumerable properties
across the entire prototype chain—baseExtremum uses an
optimized while loop:
function baseExtremum(array, iteratee, comparator) {
let index = -1;
const length = array.length;
let result;
let computed;
while (++index < length) {
const value = array[index];
const current = iteratee(value);
if (current != null && (computed === undefined
? (current === current && !isSymbol(current))
: comparator(current, computed)
)) {
computed = current;
result = value;
}
}
return result;
}Because the loop counter increments from 0 up to
length - 1, Lodash only requests numeric properties bounded
by the current length of the array. Named properties, extensions, or
custom helper functions appended to Array.prototype or
Object.prototype (such as
Array.prototype.customValue = 999999) are never
targeted.
Avoiding Property Enumeration
Standard property enumeration via for...in visits every
enumerable property on the object and its prototype chain, requiring
explicit guards like
Object.prototype.hasOwnProperty.call(array, key) to filter
out prototype extensions. Executing hasOwnProperty across
an array containing millions of elements adds severe overhead.
By utilizing indexed element retrieval (array[index]),
Lodash avoids property enumeration entirely:
- Named extensions are ignored: Prototype extensions are keyed by non-numeric strings or symbols, meaning integer iteration never requests them.
- No
hasOwnPropertyoverhead: Direct index reads bypass the need to verify ownership of each property, eliminating millions of function call frames. - Deterministic iteration bounds: The array's
lengthstrictly controls execution boundaries, ignoring prototype properties that do not alter the length counter.
Engine-Level Optimizations (V8 Fast Elements)
Directly accessing elements through sequential integer indices allows
JavaScript engines like V8 to keep the array in a "fast elements" state
(such as PACKED_SMI_ELEMENTS or
PACKED_DOUBLE_ELEMENTS).
When code uses property-enumeration loops or accesses arbitrary
prototype properties, engines may fall back to "dictionary mode" (slow
properties), which stores elements as a hash table. By sticking to dense
numeric loops, _.max enables the engine to predict memory
offsets linearly, load data into CPU caches efficiently, and inline the
comparison operations without triggering deoptimizations.
Handling of Sparse Arrays and Non-Values
In extreme edge cases involving sparse arrays (e.g.,
new Array(1000000)), accessing an unassigned index via
array[index] evaluates to undefined unless a
matching numeric index explicitly exists on
Array.prototype.
Lodash protects the calculation against these cases through its comparison checks:
- It validates that values are non-null and not
undefined. - It handles
NaNvalues via self-equality checks (current === current). - If an index does not contain a valid primitive number, the comparator ignores it, preventing faulty comparisons from setting an invalid maximum.