How Lodash propertyOf Parses Object Keys Internally
This article provides an in-depth look at how the Lodash library
executes its _.propertyOf method behind the scenes. It
covers the inner workings of Lodash's functional key lookup system,
focusing on how internal helper functions normalize, parse, memoize, and
traverse nested object properties when accessing complex data structures
dynamically.
The Role of
_.propertyOf
_.propertyOf creates a function that accepts a path and
returns the value at that path from a targeted source object. Unlike
_.get—which takes the target object and key path
simultaneously—_.propertyOf curries the target object,
creating a reusable accessor function tailored specifically for querying
that reference:
const lookup = _.propertyOf({ user: { details: { id: 101 } } });
lookup('user.details.id'); // Returns 101Internally, _.propertyOf is structured as a closure:
function propertyOf(object) {
return function(path) {
return object == null ? undefined : baseGet(object, path);
};
}If the closed-over object is null or
undefined, the accessor returns undefined
immediately. Otherwise, execution delegates to baseGet.
Path Tokenization and
castPath
The string or array representing the target key must be resolved into
an iterable sequence of property segments. Lodash routes the incoming
path through an internal function named
castPath.
- Array Check: If the provided path is already an
array of strings or symbols (e.g.,
['user', 'details', 'id']),castPathreturns it directly. - Simple Key Optimization (
isKey): Before running full string parsing, Lodash checks whether the input string is a flat, single-level property name. If the path is a symbol, a number, or a string lacking dots (.) or brackets ([]), or if the key already exists as an own/inherited property on the target object, parsing is bypassed entirely, treating the path as a single key. - Complex Path Delegation: When the path contains
delimiters like dots or brackets, it is handed off to
stringToPath.
Parsing Paths
via stringToPath and Memoization
To prevent performance degradation when resolving the same key paths
repeatedly across iterations, stringToPath wraps its
parsing logic in an internal LRU-style cache using
memoizeCachable.
When a cache miss occurs, the string is split into individual tokens using a compiled regular expression:
const rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g;This pattern matches:
- Standard identifiers separated by periods
(
user.name). - Numeric array indices enclosed in brackets
(
users[0]). - Quoted string keys inside brackets
(
object["complex-key"]). - Escaped characters within string property names.
Matched tokens are cleaned, pushed into a sequential array of string
components, stored in the memoization cache, and returned to
baseGet.
Object Traversal via
baseGet and toKey
Once the path is converted into an array of path keys,
baseGet iteratively traverses the object graph using a
while loop:
function baseGet(object, path) {
path = castPath(path, object);
let index = 0;
const length = path.length;
while (object != null && index < length) {
object = object[toKey(path[index++])];
}
return (index && index == length) ? object : undefined;
}Property Normalization with
toKey
During traversal, each segment passes through toKey.
This ensures that negative zeroes (-0) are converted to
"-0" and JavaScript Symbol primitives are
preserved without triggering unwanted string coercion errors.
Safe Navigation
The while loop checks object != null at
each iteration. If any intermediate link in the path resolves to
null or undefined, the loop terminates early.
This mechanism ensures strict runtime safety, preventing
TypeError: Cannot read property of undefined exceptions
without requiring modern optional chaining operators
(?.).
Verification
After the loop exits, (index && index == length)
verifies that traversal resolved every segment in the path sequence. If
all segments were navigated successfully, the resolved value is
returned; otherwise, it resolves to undefined.