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:

  1. Prototype Chain Traversal: The function inspects the object and climbs its prototype chain (__proto__). It continues walking upward until it reaches Object.prototype or null.
  2. Enumerability Verification: It filters properties to ensure only enumerable properties are included. Non-enumerable properties (such as built-in methods on Object.prototype like toString) are automatically ignored.
  3. 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:

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