How Lodash keysIn Retrieves Inherited Keys
This article provides an overview of how the Lodash utility library
implements _.keysIn to retrieve all enumerable property
names of an object, including those inherited from its prototype chain.
It covers the difference between _.keys and
_.keysIn, examines the underlying reliance on JavaScript's
for...in mechanism, outlines the internal Lodash functions
involved, and explains how Lodash handles environment-specific edge
cases and object coercion.
Difference Between
_.keys and _.keysIn
In Lodash, _.keys and _.keysIn serve
distinct purposes when inspecting an object's properties:
_.keys(object): Retrieves only the object's own enumerable property names. Under the hood, this relies on nativeObject.keys()._.keysIn(object): Retrieves both the object's own and its inherited enumerable property names by traversing the object's prototype chain.
const parent = { inheritedProp: 'visible' };
const child = Object.create(parent);
child.ownProp = 'also visible';
_.keys(child); // ['ownProp']
_.keysIn(child); // ['ownProp', 'inheritedProp']The Underlying Engine: The
for...in Loop
JavaScript's native for...in statement is the primary
mechanism that inspects both own and inherited enumerable properties.
While methods like Object.keys() or
Object.getOwnPropertyNames() deliberately stop at the
object itself, for...in traverses the prototype chain
automatically until it reaches Object.prototype or
null.
Lodash builds _.keysIn on top of this behavior using
internal helper methods, primarily baseKeysIn and
nativeKeysIn.
Step-by-Step Internal Execution
Input Normalization and Null Checks: When
_.keysIn(object)is called, it first checks whether the target object isnullorundefined. If so, it immediately returns an empty array[]without throwing aTypeError. If the input is a primitive (such as a string or number), it is coerced into an object wrapper so properties can be read.Array-Like and Complex Type Identification: Lodash checks whether the target is an array, an
argumentsobject, a typed array, or a buffer. For array-like collections, indexes need to be properly surfaced alongside any custom or inherited properties.Collection via
nativeKeysIn/baseKeysIn: The core collection occurs via an internal routine. In standard modern environments, Lodash runs a routine equivalent to:function nativeKeysIn(object) { const result = []; for (const key in object) { result.push(key); } return result; }Filtering and Edge-Case Sanitization: While
for...ingathers inherited keys, plain JavaScript iteration presents several browser and engine quirks that Lodash normalizes:- The
prototypeProperty: If the object is a constructor function, iterating over its prototype can produce non-standard results across different JavaScript engines. Lodash checks if the object is a prototype itself and avoids pushing the nativeconstructorkey if it is non-enumerable. - Shadowed Properties: In older JavaScript engines
(such as older V8 or Internet Explorer versions exhibiting the
DontEnumbug), certain properties liketoStringorvalueOfwere skipped infor...inloops even if explicitly overwritten. Lodash includes fallback checks to ensure that shadowed properties are appropriately identified and captured. - Index Keys on Length-Bound Structures: For strings
and array-like objects, Lodash guarantees that indices (e.g.,
'0','1') are listed consistently before generic object properties.
- The
Through these steps, _.keysIn provides a safe,
normalized array containing all accessible enumerable property names
across an object and its entire prototype hierarchy.