How Lodash hasIn Resolves Nested Prototype Chains
Lodash's _.hasIn method checks whether a specified path
exists as either a direct or inherited property on an object. This
article examines how Lodash parses nested property paths, navigates
intermediate object references, and delegates to the native JavaScript
runtime to traverse deep prototype chains.
Path Normalization and Parsing
When _.hasIn(object, path) is invoked, Lodash first
normalizes the path argument into a standardized array of
keys. If path is passed as a dot-notation string or
bracket-notation string (such as 'user.profile.settings'),
it is processed through Lodash's internal castPath and
stringToPath utilities. This splits the string into
discrete segments: ['user', 'profile', 'settings']. If an
array is already provided, Lodash uses it directly, ensuring uniform
key-by-key processing.
Step-by-Step Navigation
To verify deeply nested paths, Lodash iterates through the path segments sequentially rather than attempting to resolve the full chain in a single operation:
- Intermediate Traversal: For every path segment
prior to the final target, Lodash retrieves the intermediate value using
standard property access (equivalent to
current = current[segment]). In JavaScript, standard member access automatically walks the prototype chain ofcurrent. If an intermediate property resides on a prototype rather than the instance itself, the engine resolves it seamlessly. - Null and Undefined Checks: At each step, Lodash
verifies that the current target is non-null and not undefined. If any
intermediate value resolves to
nullorundefined, execution stops immediately, and_.hasInreturnsfalse.
Terminal Resolution via
the in Operator
Once Lodash reaches the final segment in the path array, it executes
an internal check (typically baseHasIn), which leverages
JavaScript's native in operator:
function baseHasIn(object, key) {
return object != null && key in Object(object);
}The in operator evaluates the property across the full
prototype chain of the final target:
- The JavaScript engine checks if the key exists directly on the target object.
- If it is not found, the engine follows the internal
[[Prototype]]link to the parent object. - This traversal continues up the chain until the property is
encountered or the prototype chain terminates at
null.
Handling Primitive Values
If an intermediate value or the base object is a primitive (such as a
string or number), Lodash wraps it via Object(object)
during the in check. This coercion ensures that properties
inherited from built-in prototypes—such as String.prototype
or Number.prototype—are correctly evaluated without
throwing runtime errors.
Summary of the Mechanism
_.hasIn does not manually crawl prototypes via
Object.getPrototypeOf() or __proto__. Instead,
it combines path normalization with iterative intermediate member access
and a final native in check. By letting the JavaScript
engine handle the actual prototype lookups at each segment of the path,
Lodash resolves arbitrarily deep inheritance chains reliably and
performantly.