How Lodash _.toPairsIn Handles Inherited Properties
Lodash's _.toPairsIn method converts an object into an
array of key-value pairs, explicitly capturing both own and inherited
enumerable string-keyed properties. Unlike its counterpart
_.toPairs, which restricts serialization strictly to an
object's direct properties, _.toPairsIn traverses the
target's prototype chain to extract all accessible enumerable
properties. This article explains how _.toPairsIn traverses
prototype chains, how it differs internally from standard serialization
methods, and how it resolves shadowed properties during execution.
The Traversal Mechanism
At the core of _.toPairsIn (also aliased as
_.entriesIn) is Lodash’s internal key-retrieval strategy.
While standard methods like Object.keys() or Lodash’s
_.toPairs rely on internal operations akin to
Object.prototype.hasOwnProperty, _.toPairsIn
utilizes an internal keysIn algorithm modeled around the
JavaScript for...in loop mechanics.
When you pass an object to _.toPairsIn, the method
performs the following operations:
- Prototype Chain Traversal: The function inspects
the object and climbs its prototype chain (
__proto__). It continues walking upward until it reachesObject.prototypeornull. - Enumerability Verification: It filters properties
to ensure only enumerable properties are included.
Non-enumerable properties (such as built-in methods on
Object.prototypeliketoString) are automatically ignored. - Array Mapping: For every qualified property
identifier, the method pairs the string key with the corresponding value
resolved by accessing
object[key], returning a nested array structure:[[key1, value1], [key2, value2], ...].
Shadowing and Precedence
Inherited properties behave according to standard JavaScript inheritance rules during this extraction:
- Shadowed Properties: If an object defines an own property with the exact same name as an inherited property on its prototype, the own property takes precedence. The inherited property is shadowed, meaning the resulting pair array includes the key only once, containing the child object's value.
- Property Overrides: If multiple levels in the prototype chain define the same property name, the value closest to the instance in the chain is resolved.
Code Comparison:
_.toPairs vs. _.toPairsIn
Consider the following prototype hierarchy:
const proto = {
inheritedProp: 'from prototype',
shared: 'default'
};
const instance = Object.create(proto);
instance.ownProp = 'from instance';
instance.shared = 'custom'; // Shadows proto.shared
// Standard pair extraction (own properties only)
_.toPairs(instance);
// Returns:
// [ ['ownProp', 'from instance'], ['shared', 'custom'] ]
// Inherited pair extraction (traverses prototype)
_.toPairsIn(instance);
// Returns:
// [
// ['ownProp', 'from instance'],
// ['shared', 'custom'],
// ['inheritedProp', 'from prototype']
// ]Key Considerations
- Symbol Keys:
_.toPairsInonly extracts string-keyed properties. Inherited or ownSymbolkeys are not included in the output. - Performance Impact: Traversing long prototype
chains requires more overhead than reading own properties directly via
_.toPairs. If deep prototypes contain numerous enumerable properties, the serialization cost increases proportionally. - Enumerable Requirement: If an inherited property
was created via
Object.definePropertywithenumerable: false,_.toPairsInwill ignore it entirely.