How Lodash defaultsDeep Handles Undefined Values
This article examines how the _.defaultsDeep function in
the Lodash JavaScript library handles properties explicitly set to
undefined within a destination object. It details the
underlying evaluation mechanism, demonstrates the behavior through code
examples, and explains the functional difference between
undefined and other falsy values when applying recursive
default settings.
In Lodash, _.defaultsDeep recursively assigns enumerable
string-keyed properties of source objects to the destination object.
When evaluating whether to assign a property from a source object,
_.defaultsDeep checks whether the corresponding property on
the destination object resolves to undefined.
If a destination property is explicitly set to undefined
(for example, { a: undefined }),
_.defaultsDeep treats it as if the property does not exist
or has not been populated. Consequently, the function overwrites the
undefined value with the matching value from the source
object.
Consider the following example:
const _ = require('lodash');
const destination = {
user: {
name: undefined,
role: 'editor'
}
};
const source = {
user: {
name: 'Anonymous',
role: 'subscriber',
theme: 'dark'
}
};
const result = _.defaultsDeep(destination, source);
console.log(result);
// Output:
// {
// user: {
// name: 'Anonymous',
// role: 'editor',
// theme: 'dark'
// }
// }In this scenario, destination.user.name is explicitly
set to undefined. Because the property resolves to
undefined, _.defaultsDeep applies the source
value 'Anonymous' to the destination. Conversely,
destination.user.role is already defined as
'editor', so the source value 'subscriber' is
ignored.
This behavior applies at every level of nested objects. If a nested
object in the destination contains explicit undefined
properties, _.defaultsDeep continues its traversal and
replaces those values with the source properties at the identical
path.
It is important to distinguish undefined from other
falsy values in JavaScript, such as null,
false, 0, or empty strings ("").
The _.defaultsDeep function strictly checks for
undefined. If a destination property is set to
null or any other falsy value, it is considered defined,
and the source object will not overwrite it:
const destinationWithNull = {
user: {
name: null
}
};
const sourceFallback = {
user: {
name: 'Anonymous'
}
};
const nullResult = _.defaultsDeep(destinationWithNull, sourceFallback);
console.log(nullResult);
// Output:
// {
// user: {
// name: null
// }
// }Unlike native operations such as Object.assign() or the
object spread operator (...), which copy explicit
undefined values directly into the target,
_.defaultsDeep views undefined as a trigger to
pull fallback values from the source.