Lodash _.every on Uninitialized Array Slots

When evaluating an array composed entirely of uninitialized slots (a sparse array such as new Array(3)), Lodash's _.every method returns false by default. This outcome often surprises developers because native JavaScript methods handle sparse arrays differently. This guide explains why _.every yields false, how it treats empty array slots compared to native JavaScript, and how custom predicate functions alter the result.

The Default Return Value: false

When you pass an array with purely uninitialized slots to _.every without a custom predicate, Lodash uses its default iteratee, _.identity. The call evaluates to false:

const sparseArray = new Array(3); // [ <3 empty items> ]

_.every(sparseArray); 
// => false

Why Lodash Returns false

Lodash processes arrays using a standard index-based loop over the array's length property rather than checking if an index exists on the object.

  1. Accessing Missing Indices: In JavaScript, attempting to access an empty or uninitialized slot via bracket notation (e.g., sparseArray[0]) does not throw an error; it evaluates to undefined.
  2. Default Iteratee (_.identity): When no predicate is provided, Lodash defaults to _.identity, which returns the value of the current element.
  3. Falsy Check: Lodash evaluates the first slot, encounters undefined, and checks its truthiness. Because undefined is falsy, the condition fails immediately, causing _.every to short-circuit and return false.

Difference Between Lodash and Native Array.prototype.every

The behavior of _.every directly contrasts with ECMAScript's native Array.prototype.every.

Native array iteration methods check whether each index actually exists in the array using the internal HasProperty check. Because uninitialized slots have no assigned index property, the native method skips them entirely:

const sparseArray = new Array(3);

// Native JavaScript
sparseArray.every(() => false);
// => true

In native JavaScript, the callback function is never executed because there are no initialized slots to visit. Under the rules of vacuous truth, native Array.prototype.every returns true. Lodash does not skip these slots, treating each empty position as an existing element with the value undefined.

The Impact of Custom Predicates

If you supply a custom predicate function to _.every, the return value depends entirely on how your predicate handles undefined.

If the predicate considers undefined to be valid, _.every returns true:

const sparseArray = new Array(3);

// Explicitly checking for undefined
_.every(sparseArray, (value) => value === undefined);
// => true

// Predicate that always returns true
_.every(sparseArray, () => true);
// => true

If the predicate expects defined, truthy, or specific non-undefined values, it returns false:

// Checking for a specific data type
_.every(sparseArray, _.isNumber);
// => false