Lodash _.isArray vs Array.isArray Polyfill

In modern JavaScript development, Lodash's _.isArray and the native Array.isArray method are often treated interchangeably, but understanding how Lodash implements its fallback polyfill reveals important distinctions. While current versions of Lodash bind directly to the native Array.isArray when available, its internal fallback mechanism differs from the native implementation in how it verifies object types, handles cross-realm objects, protects against ES6 Symbol.toStringTag spoofing, and resolves JavaScript proxies.

The Direct Reference vs. Fallback Mechanism

In modern Lodash environments (Lodash v4 and later), _.isArray is defined simply as:

var isArray = Array.isArray;
module.exports = isArray;

When running in an environment with full ECMAScript 5+ support, _.isArray is an exact reference to native Array.isArray. However, in legacy builds, standalone implementations, or environments where native support is absent, Lodash falls back to an internal polyfill (often structured around baseIsArray).

The standard polyfill implementation relies on string tag inspection:

function isArray(value) {
  return isObjectLike(value) && baseGetTag(value) == '[object Array]';
}

Internal Slot Verification vs. Tag Checking

The fundamental difference between the native method and the polyfill lies in how the runtime determines that an entity is an array:

Resistance to Symbol.toStringTag Spoofing

In ECMAScript 2015 (ES6) and later, developers can customize object tag representations using Symbol.toStringTag:

const fakeArray = {
  [Symbol.toStringTag]: 'Array'
};

A rudimentary polyfill using Object.prototype.toString.call(fakeArray) evaluates to '[object Array]', falsely identifying a plain object as an array.

Cross-Realm and Proxy Behavior

Both native Array.isArray and Lodash's polyfill successfully identify arrays created across different execution contexts, such as an <iframe> or Node.js vm module. Because separate realms possess different Array.prototype instances, instanceof Array fails, but both Array.isArray and Object.prototype.toString.call return true.

However, Proxy handling differs:

Execution Performance

Native Array.isArray is implemented at the engine level (C++ in V8 and SpiderMonkey) and is recognized by JIT compilers as an intrinsic operation. It compiles down to efficient machine code instructions.

The polyfill approach requires multiple userland steps:

  1. Checking if the value is object-like.
  2. Handling null and undefined checks.
  3. Invoking Object.prototype.toString (or Lodash's baseGetTag).
  4. Allocating and comparing strings.

While the modern _.isArray method executes with native speed by aliasing Array.isArray, Lodash's internal polyfill logic provides a defensive, tag-scrubbing fallback designed for legacy compatibility.