Lodash toPlainObject: Flatten Inherited Properties
Lodash’s _.toPlainObject converts any input value into a
plain JavaScript object by flattening its inherited prototype chain into
direct, own enumerable properties. While standard JavaScript operations
like Object.assign() or object spread
({ ...obj }) copy only an object's own properties,
_.toPlainObject traverses enumerable properties across the
entire inheritance hierarchy and assigns them as own properties of a
newly created plain object.
The Mechanism of
_.toPlainObject
In JavaScript, objects created via classes or constructor functions
inherit properties and methods through the prototype chain.
These inherited members do not reside directly on the instance, meaning
methods like Object.prototype.hasOwnProperty() return
false for them.
When _.toPlainObject(value) is executed, Lodash performs
the following sequence:
- Creates a Clean Target: Lodash initializes a new,
plain object literal (
{}) without carrying over the prototype of the source instance. - Traverses Enumerable Properties: Unlike
Object.keys(), which only reads own properties, Lodash scans both own and inherited enumerable properties. Internally, this operates similarly to afor...inloop. - Flattens Values to Own Properties: Each enumerable key discovered along the prototype chain is assigned directly onto the new target object as an own property.
- Stops at the Base Prototype: The traversal halts
when reaching standard built-in prototype layers, such as
Object.prototype, avoiding internal native properties.
Code Demonstration
Consider a scenario where a subclass inherits properties from a parent constructor:
const _ = require('lodash');
function Parent() {
this.parentOwn = 'parent value';
}
Parent.prototype.inheritedFromParent = 'inherited parent value';
function Child() {
Parent.call(this);
this.childOwn = 'child value';
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
Child.prototype.inheritedFromChild = 'inherited child value';
const instance = new Child();
// Standard JavaScript spread only captures own properties
const spreadObject = { ...instance };
console.log(spreadObject.hasOwnProperty('inheritedFromChild')); // false
// Lodash toPlainObject flattens inherited properties
const plainObject = _.toPlainObject(instance);
console.log(plainObject.hasOwnProperty('inheritedFromChild')); // true
console.log(plainObject.hasOwnProperty('inheritedFromParent')); // true
console.log(plainObject);
// Output:
// {
// parentOwn: 'parent value',
// childOwn: 'child value',
// inheritedFromChild: 'inherited child value',
// inheritedFromParent: 'inherited parent value'
// }Key Differences and Limitations
- Enumerability Requirement:
_.toPlainObjectonly extracts enumerable properties. Methods defined within ES6classbodies are non-enumerable by default and will not be copied over. - Property Overwrites: If a child object defines an own property with the same name as an inherited prototype property, the child's own property value takes precedence during the flattening process.
- Loss of Methods and Prototypes: The returned object
loses its original prototype link (
instanceof Childbecomesfalse). Dynamic getter and setter behaviors defined on prototypes will resolve to static values at the moment of conversion.