How Lodash _.forIn Iterates Inherited Properties
Lodash's _.forIn method traverses an object's enumerable
properties, encompassing both direct ("own") properties and those
inherited through its prototype chain. By wrapping native JavaScript
iteration behaviors and coupling them with customizable iteratee
execution, _.forIn provides a reliable, cross-environment
way to process inherited states. This article explains the underlying
mechanism of _.forIn, examines how it differs from methods
like _.forOwn, and illustrates its mechanics with practical
examples.
The Core Mechanism Behind
_.forIn
At its core, _.forIn relies on the native JavaScript
for...in statement, which is inherently designed to
traverse both own and inherited enumerable properties across an object's
prototype chain.
Internally, Lodash builds _.forIn using higher-order
factory functions, primarily createBaseFor() and
baseFor(). Unlike standard array or object traversal
functions in Lodash that extract keys into an array using
Object.keys() (which only retrieves an object’s own
enumerable properties), _.forIn avoids eager key
extraction. Instead, it delegates the traversal directly to an iteration
loop that allows JavaScript's prototype lookup resolution to evaluate
every enumerable property in the chain.
The basic internal flow operates as follows:
- Object Validation: Lodash casts or normalizes the target input to ensure it can be treated as an object.
- Loop Execution: A specialized loop walks over the object using native prototype-traversing iteration logic.
- Iteratee Invocation: For each property, the
callback iteratee is invoked with three arguments:
(value, key, object). - Early Exit Support: If the iteratee explicitly
returns
false,_.forInhalts iteration immediately, offering an optimization not natively available in standardfor...instatements without explicitbreakhandling.
_.forIn vs.
_.forOwn
The primary distinction between _.forIn and
_.forOwn is the filtering of inherited properties:
_.forOwn: Uses an internal check equivalent toObject.prototype.hasOwnProperty.call(object, key)before invoking the iteratee. This limits processing strictly to properties defined directly on the target object itself._.forIn: Intentionally omits thehasOwnPropertycheck. Any property marked asenumerable: trueanywhere along the target's prototype chain is processed.
Practical Code Example
Consider an example using object inheritance through
Object.create():
const _ = require('lodash');
// Define a prototype object
const vehicle = {
hasWheels: true,
drive() {
return 'Moving';
}
};
// Create a new object inheriting from vehicle
const car = Object.create(vehicle);
car.make = 'Toyota';
car.model = 'Corolla';
// Using _.forIn to iterate
_.forIn(car, (value, key) => {
console.log(`${key}: ${value}`);
});Output:
make: Toyota
model: Corolla
hasWheels: true
drive: [Function: drive]
In this example, make and model are own
properties of car. hasWheels and
drive are inherited from vehicle. Because
_.forIn does not enforce property ownership, all four keys
are visited.
Iteration Rules and Caveats
When utilizing _.forIn, several behaviors of JavaScript
prototype iteration apply:
- Enumerable Only: Properties set with
enumerable: false(such as native methods onObject.prototypeliketoStringor properties configured viaObject.defineProperty) are ignored. - Symbol Properties: Like the native
for...inloop,_.forInignores properties keyed bySymbolprimitives. - Property Shadowing: If an object defines an own property with the exact same key as an inherited prototype property, the own property takes precedence and is processed once. The shadowed prototype property will not be evaluated again.
- Order of Traversal: Modern JavaScript engines prioritize own keys first, followed by keys further up the prototype chain in order of traversal, respecting creation and enumeration order.