How Lodash _.has Checks for Direct Properties
In JavaScript, verifying property ownership on an object is a
frequent necessity, and the Lodash library provides the
_.has method specifically to determine if a path is a
direct property of an object. This article examines the internal
mechanics of _.has, how it safely verifies property
ownership via Object.prototype.hasOwnProperty, how it
processes nested path strings and arrays, and how it differs from
prototype-inclusive checks like _.hasIn.
The Core
Mechanism: Direct Ownership via hasOwnProperty
The primary purpose of _.has(object, path) is to verify
that a property belongs directly to an object rather than being
inherited through its prototype chain.
Native JavaScript provides
Object.prototype.hasOwnProperty, but calling it directly on
an object (such as obj.hasOwnProperty(prop)) can lead to
runtime exceptions if the object was created with
Object.create(null) or if the hasOwnProperty
method has been overwritten. Lodash circumvents these risks by safely
invoking the method using
Object.prototype.hasOwnProperty.call(object, key) via its
internal utilities.
const proto = { inheritedProp: 'value' };
const obj = Object.create(proto);
obj.directProp = 'value';
_.has(obj, 'directProp'); // true
_.has(obj, 'inheritedProp'); // falseDeep Path Traversal
Unlike native property checks, _.has natively supports
deep property paths represented either as dot-notation strings (e.g.,
'user.profile.name') or arrays of keys (e.g.,
['user', 'profile', 'name']).
When checking deep paths, Lodash processes the input through the following steps:
- Path Normalization: The path is converted into an
array of segments using an internal
toPathparser. - Sequential Verification: Lodash traverses the object structure one segment at a time.
- Safety Checks: At each segment, Lodash checks if the current target is an object and whether the current segment exists directly on that target.
- Early Termination: If any intermediate segment
evaluates to
null,undefined, or does not exist as a direct property, traversal halts immediately and the method returnsfalse, preventingTypeErrorexceptions.
const user = {
profile: {
details: {
id: 101
}
}
};
_.has(user, 'profile.details.id'); // true
_.has(user, ['profile', 'details']); // true
_.has(user, 'profile.settings.theme'); // false (safely returns false)_.has vs. _.hasIn
Understanding how _.has functions requires
distinguishing it from Lodash’s counterpart method,
_.hasIn.
_.haschecks solely for direct (own) properties. It acts as a safe, path-traversing wrapper aroundhasOwnProperty._.hasInchecks for both direct and inherited properties. It mimics the native JavaScriptinoperator while supporting nested paths.
function Person() {}
Person.prototype.species = 'Homo sapiens';
const individual = new Person();
individual.name = 'Alice';
_.has(individual, 'species'); // false (inherited from prototype)
_.hasIn(individual, 'species'); // true (found on prototype chain)Through this architecture, _.has delivers a defensive,
fail-safe mechanism to validate direct property existence across shallow
and deeply nested data structures.