How Lodash invokeMap Works Without Mutating Objects
Lodash's _.invokeMap allows developers to call a
specific method on each element in a collection, returning the results
in a newly constructed array. This article breaks down the internal
mechanics of _.invokeMap, explaining how it executes method
calls natively via standard JavaScript invocation patterns, manages
duplicate object references without side effects, and guarantees that
the underlying collection and its references remain structurally
unmutated.
The Internal
Architecture of _.invokeMap
At its core, _.invokeMap delegates collection processing
through internal helper functions, primarily baseEach and
baseInvoke. When invoked, the function does not alter the
incoming collection. Instead, it inspects the collection type and
initializes a brand-new array to store the execution results:
var result = isArrayLike(collection) ? Array(collection.length) : [];By pre-allocating or instantiating an entirely distinct array for the return values, Lodash ensures that the structure of the input collection is isolated from the start.
Native Dispatch via Function Application
To execute the target method natively, _.invokeMap
determines whether the method identifier is a direct function reference
or a path string.
- Direct Function: If the path parameter is a
function, Lodash invokes it directly against the current element using
standard JavaScript function binding:
path.apply(value, args); - Path String: If a property path is supplied (such
as
'toUpperCase'or a deep path like'nested.method'), the internal utilitybaseInvokenavigates the object’s property chain natively to resolve the function reference. Once resolved, it calls the method in the context of the parent object usingfunc.apply(parent, args).
Because Lodash relies on native
Function.prototype.apply, the method execution operates
within the standard ECMAScript runtime dispatch mechanism. Lodash
creates no wrappers, proxies, or synthetic execution contexts around the
target object.
Handling Identically Referenced Objects
A common concern in functional operations is how an engine behaves when multiple indices point to the exact same object reference:
const shared = { getValue: () => 42 };
const list = [shared, shared];When _.invokeMap(list, 'getValue') executes, Lodash
processes each element iteratively using an internal counter:
- Lodash passes the reference at index
0(shared) to the invocation pipeline. - The method is called natively, and the returned value is assigned to
the output container:
result[0] = 42. - The iteration proceeds to index
1, reading the identical reference (shared). - The method is called again, and the return value is assigned to
result[1] = 42.
Because _.invokeMap writes outputs strictly to the newly
allocated result array indices rather than setting
properties on the iterated items, identical references cannot
cross-contaminate one another through Lodash's internal logic.
Preventing Mutation
Lodash guarantees non-mutation through three specific structural constraints:
- Target Immutability: The source collection is
strictly read; all assignment operations target the newly initialized
return array via
result[++index] = .... - Stateless Method Resolution: Property lookups
performed by
baseInvokeaccess the object prototype or own properties in read-only operations (parent[key]), preventing property redefinition or metadata modification. - Reference Preservation: Identically referenced
objects remain untouched in memory. Unless the invoked method itself
explicitly alters
thisinternally, Lodash’s execution layer performs purely functional derivation without modifying the source objects.