Lodash cloneDeepWith Customizer Guide
The _.cloneDeepWith method in Lodash provides a way to
recursively clone JavaScript values while selectively customizing how
specific properties or types are copied. By passing a customizer
callback function, you can intercept the recursive cloning algorithm to
preserve instances, omit sensitive fields, or handle unsupported types
like DOM nodes and custom classes. This article details the signature of
the customizer function, its arguments, return rules, and practical
implementation patterns.
The Customizer Function Signature
The customizer function passed to
_.cloneDeepWith(value, [customizer]) is invoked for each
value traversed during the cloning process. It accepts up to four
arguments:
function customizer(value, key, object, stack)value: The current value being cloned.key: The key or property name of the value within its parent object (orundefinedfor the root element).object: The parent object containing the current value (orundefinedfor the root element).stack: An internal Lodash structure used to track traversed objects and prevent circular reference loops.
Return Values and Fallback Behavior
The behavior of _.cloneDeepWith depends strictly on what
the customizer returns:
- Returning
undefined: Instructs Lodash to ignore the customizer for this node and proceed with standard deep-cloning logic. If you do not explicitly return a value, JavaScript functions implicitly returnundefined, letting Lodash handle the rest of the object tree automatically. - Returning any other value: Instructs Lodash to use that exact returned value as the clone for that node, stopping further recursive traversal down that particular property branch.
Common Implementation Patterns
1. Preserving Instances or Special Types
By default, _.cloneDeep converts custom class instances
into plain objects. A customizer can retain references to specific
instances or handle them using native cloning mechanisms:
const _ = require('lodash');
function preserveInstances(value) {
// Preserve DOM nodes by reference or clone via native cloneNode
if (_.isElement(value)) {
return value.cloneNode(true);
}
// Preserve instances of a specific custom class
if (value instanceof CustomHandler) {
return new CustomHandler(value.settings);
}
// Fall back to default deep-cloning
return undefined;
}
const cloned = _.cloneDeepWith(data, preserveInstances);2. Modifying Values Based on Keys
The customizer can inspect the key argument to
selectively mutate, mask, or omit specific fields:
const _ = require('lodash');
function maskSensitiveData(value, key) {
if (key === 'password' || key === 'token') {
return '[REDACTED]';
}
return undefined;
}
const user = {
id: 101,
profile: {
username: 'jdoe',
password: 'supersecretpassword'
}
};
const sanitizedUser = _.cloneDeepWith(user, maskSensitiveData);3. Handling Custom Data Types (e.g., RegEx or BigInt)
While modern versions of Lodash handle many built-in types, customizers can enforce specific copy logic for distinct primitives or data structures:
const _ = require('lodash');
function handleCustomTypes(value) {
if (typeof value === 'bigint') {
return BigInt(value.toString());
}
return undefined;
}
const payload = { id: 1n, nested: { count: 42n } };
const clonedPayload = _.cloneDeepWith(payload, handleCustomTypes);