Lodash _.create Property Descriptors Explained
This article examines how the Lodash _.create method
handles property descriptors when instantiating new objects from a
specified prototype. While JavaScript's native
Object.create accepts explicit property descriptor maps,
Lodash's utility processes properties differently, resulting in standard
data property attributes rather than customized descriptor
configurations.
Native
Object.create vs. Lodash _.create
To understand how descriptors are applied in _.create,
it is essential to contrast it with native JavaScript:
- Native
Object.create(proto, [propertiesObject]): The second argument expects an object containing explicit property descriptors (objects definingvalue,writable,enumerable,configurable,get, orset). Properties created via this native method default to non-writable, non-enumerable, and non-configurable if attributes are omitted. - Lodash
_.create(prototype, [properties]): The second argument does not accept property descriptor definitions. Instead, it expects a plain object containing regular key-value pairs.
Descriptors Applied by
_.create
Lodash's internal implementation uses prototype inheritance
(typically falling back on native Object.create(prototype))
and then copies own enumerable string-keyed properties using standard
assignment semantics (baseAssign).
Because properties are assigned directly via property assignment
rather than through Object.defineProperty, JavaScript
attaches the default data descriptor configuration for standard
assignments:
value: The assigned value provided in the properties object.writable:trueenumerable:trueconfigurable:true
Lodash does not attach or enforce any explicit, restricted property descriptors (such as read-only or non-enumerable flags) on the newly created object.
Passing Descriptor
Objects to _.create
If you pass a descriptor definition object to _.create
as you would with native Object.create, Lodash treats the
entire descriptor map as a literal value:
const proto = { greet() { return 'hello'; } };
// Incorrect expectation based on native Object.create:
const obj = _.create(proto, {
name: {
value: 'Alice',
writable: false,
enumerable: false
}
});
// Resulting descriptor for 'name':
// Object.getOwnPropertyDescriptor(obj, 'name')
// returns:
// {
// value: { value: 'Alice', writable: false, enumerable: false },
// writable: true,
// enumerable: true,
// configurable: true
// }In this scenario, obj.name evaluates to the descriptor
object itself rather than the string 'Alice', and the outer
property name remains fully writable, enumerable, and
configurable.
Summary
When _.create builds an object from a specified
prototype:
- It attaches no explicit, customized property descriptors.
- It populates properties via standard assignment, applying default
descriptor states (
writable: true,enumerable: true,configurable: true). - To define custom flags or accessors
(
get/set), developers must useObject.definePropertyor nativeObject.createinstead of_.create.