Does Lodash mapKeys Mutate Original Objects?
This article examines the structural behavior of Lodash’s
_.mapKeys method and its impact on source object references
during execution. It explains whether structural mutations occur on the
original reference, how key transformations are instantiated, and how
value references are handled across the iteration lifecycle.
Structural Mutation
Behavior of _.mapKeys
When _.mapKeys executes, it applies zero
structural mutations to the original object reference.
In JavaScript and Lodash design paradigms, _.mapKeys is
an immutable transformation function. It does not delete, rename,
reorder, or add properties to the input object passed as its first
argument. The original object's prototype, property descriptors, and own
enumerable properties remain completely untouched throughout and after
the iteration loop.
How _.mapKeys
Executes Internally
To understand why no structural mutation occurs, it helps to examine how the iteration operates:
- Initialization of a New Object: Before iterating,
Lodash initializes a completely new, empty object target
(
{}). - Iteration Over Keys: Lodash traverses the own
enumerable string-keyed properties of the source object using an
internal iteration utility (similar to
baseForOwn). - Iteratee Invocation: For each property, the
provided iteratee callback is invoked with three arguments:
(value, key, object). - Assignment to the Target: The return value of the iteratee is cast to a string and assigned as a key on the newly created object, with the corresponding value from the original property assigned to that key.
- Return Value: Once iteration completes, the newly constructed object is returned. The original reference is never targeted for property assignment.
const original = { a: 1, b: 2 };
const transformed = _.mapKeys(original, (value, key) => key + '_new');
console.log(original); // { a: 1, b: 2 } (completely unchanged)
console.log(transformed); // { a_new: 1, b_new: 2 }
console.log(original === transformed); // falseShallow Reference Copying of Property Values
While the structure (the keys and the object wrapper) of the original
object is preserved without mutation, _.mapKeys performs a
shallow copy of the property values:
- Primitive Values: Strings, numbers, booleans, symbols, null, and undefined are copied by value into the new object.
- Reference Values: Objects, arrays, and functions are copied by reference.
Because values are copied by reference, both the original object and the newly generated object point to the exact same memory locations for nested structures. Modifying a nested object via either reference will reflect in both places:
const original = { user: { name: 'Alice' } };
const mapped = _.mapKeys(original, (value, key) => key.toUpperCase());
// Structural keys differ:
// original has 'user', mapped has 'USER'
// Modifying nested property via the new object:
mapped.USER.name = 'Bob';
// The nested mutation reflects in the original:
console.log(original.user.name); // 'Bob'Potential Mutations via Custom Iteratee Callbacks
Although _.mapKeys itself does not perform structural
mutations, the iteratee function receives the original object as its
third parameter: iteratee(value, key, object).
If a developer explicitly writes logic inside the iteratee that
mutates the received object argument (e.g.,
delete object[key] or object[key] = null),
structural mutations will occur as a direct result of user-defined side
effects, not from the mechanics of Lodash itself.