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:
- Native
Array.isArray: Checks for the presence of the internal[[DefineOwnProperty]]exotic array behavior or internal slots in the JavaScript engine. It does not inspect prototype strings. - Lodash Fallback: Checks that the value is an object
(non-null and
typeofis'object') and evaluates its internal tag viaObject.prototype.toString.call(value).
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.
- Native
Array.isArray: Completely ignoresSymbol.toStringTagbecause it inspects internal engine slots, correctly identifyingfakeArrayasfalse. - Lodash Polyfill: Uses a specialized internal
utility (
baseGetTag) that detects whether an object has an ownSymbol.toStringTagproperty. If present, it temporarily conceals or unmasks the symbol to read the true internal[[Class]], minimizing the risk of spoofing before comparing the result to'[object 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:
- Native
Array.isArray: Automatically unwraps aProxyobject recursively to evaluate whether the underlying target is an array. If a proxy targets an array, nativeArray.isArray(proxy)evaluates totrue. - Userland Polyfills: If a Proxy traps property
access or prototype lookups, a pure JavaScript polyfill may not properly
access the underlying target if proxy traps interfere with
Object.prototype.toString.
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:
- Checking if the value is object-like.
- Handling null and undefined checks.
- Invoking
Object.prototype.toString(or Lodash'sbaseGetTag). - 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.