Lodash Fallback Mechanism When Native Set Is Missing

When running in legacy JavaScript environments where the ECMAScript 2015 (ES6) native Set object is unavailable, the Lodash library falls back to an internal tiered caching architecture. Primarily managed through an internal constructor called SetCache, Lodash delegates storage and lookup operations to MapCache. This mechanism allows functions requiring uniqueness checks—such as _.uniq, _.difference, and _.intersection—to function reliably without throwing runtime errors, gracefully degrading lookup performance from constant time \(O(1)\) to linear time \(O(n)\) when dealing with complex objects.

Feature Detection with getNative

Before utilizing native modern features, Lodash checks the runtime environment using an internal utility called getNative.

var Set = getNative(root, 'Set');

The getNative function verifies that Set exists on the global object (window, global, or self) and ensures its implementation consists of native code rather than an incomplete or incompatible userland polyfill. If this check fails, Lodash sets its internal reference to undefined and switches to polyfilled data structures.

The Role of SetCache

Methods that rely on set operations do not interface directly with native arrays or modern sets. Instead, Lodash routes collection data through an internal SetCache class.

When Set is natively available, SetCache creates an instance of native Set to store values. When Set is unavailable, SetCache initializes an internal instance of MapCache:

function SetCache(values) {
  var index = -1,
      length = values ? values.length : 0;

  this.__data__ = new MapCache;
  while (++index < length) {
    this.add(values[index]);
  }
}

Because a set only requires checking for key existence, SetCache.prototype.add stores the item as a key inside MapCache paired with a constant dummy value (HASH_UNDEFINED).

The MapCache Fallback Tiers

The MapCache implementation handles missing native collections through a composite data structure composed of two main fallbacks:

  1. Hash Cache: Used for primitive keys such as strings, numbers, and symbols. It avoids prototype pollution by utilizing Object.create(null) or plain objects with stripped prototypes. Lookups against the Hash cache remain near \(O(1)\) complexity because keys map directly to object properties.

  2. ListCache: Used when keys are complex objects or reference types that cannot safely be converted to string keys without collisions. ListCache stores entries in an internal array of two-element arrays ([[key, value]]). Lookups are performed via linear search using the SameValueZero comparison algorithm. In the absence of native Set or Map, object deduplication drops to \(O(n)\) search time per item, leading to \(O(n^2)\) overall performance for utilities like _.uniq.

Iteration and Array Scanning Fallbacks

In certain lightweight operations or small collection sizes (typically fewer than 200 elements), Lodash skips cache initialization entirely. Instead, it relies on direct array scanning utilities like arrayIncludes and arrayIncludesWith. These functions use simple iteration loops over native arrays, bypassing the overhead of creating fallback cache instances when processing small data sets.