How Lodash cloneWith Handles Prototype Properties

This article examines how Lodash’s _.cloneWith method evaluates its customizer function with respect to object prototypes and inherited properties. It explains how Lodash isolates own properties from the prototype chain during cloning, clarifies why inherited prototype properties are never passed directly to the customizer, and demonstrates how developers can still customize prototype behavior during cloning operations.

Own Properties vs. Inherited Prototype Properties

When _.cloneWith processes an object, it restricts property copying strictly to the object's own enumerable properties. Under the hood, Lodash utilizes internal mechanisms (equivalent to Object.keys or Reflect.ownKeys) rather than a for...in loop that traverses up the prototype chain.

Because inherited properties residing on the prototype chain are not considered own properties, the customizer function is never invoked for individual prototype properties. The customizer arguments (value, key, object, stack) will only receive values that exist directly on the target instance itself.

Prototype Preservation in the Default Cloning Path

If the customizer returns undefined, _.cloneWith falls back to Lodash's internal baseClone routine. For objects with custom prototypes (such as instances of classes or constructor functions):

  1. Instantiation: Lodash initializes the cloned target by preserving the prototype, typically via Object.create(Object.getPrototypeOf(object)) or by initializing an instance via its original constructor.
  2. Property Assignment: It then assigns the object's own properties to this newly initialized clone.

Because prototype properties remain attached to the prototype object rather than copied onto the instance, their behavior, getters, setters, and inherited methods remain shared through prototype delegation.

How the Customizer Evaluates the Root Object

While the customizer does not fire for individual prototype properties, it is evaluated for the root object itself. This is where prototype handling can be explicitly altered.

When customizer(value) is called on the object:

Behavior in Deep Cloning (_.cloneDeepWith)

It is also important to contrast this with _.cloneDeepWith. Even during a recursive deep clone, the customizer descends solely into nested own properties. Properties defined on an object’s prototype or __proto__ remain untouched by the customizer throughout the entire depth of the traversal, ensuring prototype chains are not accidentally flattened into own properties on clones.