Object.create()
Object.create(proto, propertiesObject?) Object.create() creates a new object with a specified prototype object and optional property descriptors. This method is fundamental to JavaScript’s prototypal inheritance, allowing developers to explicitly set the prototype of an object at creation time rather than using constructor functions or Object.setPrototypeOf().
Syntax
Object.create(proto)
Object.create(proto, propertiesObject)
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
proto | object | null | The object which will be the prototype of the newly created object. Can be null for objects without a prototype. |
propertiesObject | object | undefined | An optional object whose enumerable own properties specify property descriptors to be added to the new object. |
Examples
Creating an object with a custom prototype
const personPrototype = {
greet() {
return `Hello, my name is ${this.name}`;
}
};
const person = Object.create(personPrototype);
person.name = 'Alice';
person.age = 30;
console.log(person.greet());
// Hello, my name is Alice
console.log(Object.getPrototypeOf(person) === personPrototype);
// true
Creating an object with property descriptors
The second argument to Object.create() gives you fine-grained control through property descriptors rather than plain assignment. Each descriptor spells out whether a property is writable, enumerable, and configurable, which matters when you want to lock parts of an object down. The example below creates a two-field object where version is read-only and cannot be reconfigured, while name remains writable.
const obj = Object.create(null, {
name: {
value: 'JavaScript',
writable: true,
enumerable: true,
configurable: true
},
version: {
value: 'ES5+',
writable: false,
enumerable: true,
configurable: false
}
});
console.log(obj.name);
// JavaScript
obj.name = 'TypeScript';
console.log(obj.name);
// TypeScript (writable: true)
console.log(obj.version);
// ES5+ (writable: false, cannot be changed)
Creating a null prototype object
Passing null as the prototype strips away every inherited method from Object.prototype. Objects created this way have no toString, no hasOwnProperty, and no constructor — they are pure key-value stores. That makes them ideal for dictionaries and caches where key names might collide with inherited property names, but it also means you must reach for static helpers like Object.hasOwn() instead of instance methods.
const nullProto = Object.create(null);
nullProto.message = 'Hello';
console.log(Object.getPrototypeOf(nullProto));
// null
console.log(nullProto.hasOwnProperty);
// undefined - no inherited methods from Object.prototype
Common Patterns
Factory pattern with Object.create():
A factory that uses Object.create() can produce objects with shared behavior without calling new or relying on a constructor function. The prototype carries the methods, while each instance gets its own data properties through descriptors. This pattern keeps the prototype chain explicit and avoids the confusion that this binding sometimes causes in constructor-based code.
function createAnimal(name, species) {
return Object.create(Animal.prototype, {
name: { value: name, writable: true, enumerable: true },
species: { value: species, writable: true, enumerable: true }
});
}
const dog = createAnimal('Buddy', 'Dog');
console.log(dog.species); // Dog
Object pooling for performance-critical code:
Pre-allocating objects with Object.create(null) keeps the garbage collector quiet in hot loops. Each pool slot is a lightweight dictionary with no prototype overhead, and the factory reuses slots instead of allocating fresh objects. In game loops, animation frames, or real-time data feeds, avoiding per-frame allocations can be the difference between a smooth 60 fps and visible jank.
const reusablePool = Object.create(null);
let poolIndex = 0;
const poolSize = 100;
function getFromPool() {
if (poolIndex < poolSize) {
return reusablePool[poolIndex++];
}
return Object.create(null);
}
Object.create() vs class syntax
ES6 classes are syntactic sugar over prototype-based inheritance, and Object.create() is the lower-level mechanism they build on. Using classes is generally preferred for readability and tooling support. However, Object.create() is still useful when you need fine-grained control over property descriptors at creation time, or when implementing prototypal delegation patterns without the class overhead.
Why Object.create() is useful
Object.create() gives you a direct way to separate behavior from data. That is helpful when several objects should share the same methods but keep their own state. Instead of copying functions onto every instance, you put them on the prototype and let each object inherit them. The result is compact, explicit, and easy to inspect with the prototype APIs.
Null prototypes and safety
Creating an object with null as the prototype removes inherited helper methods and inherited property names from the picture. That is handy for dictionary-style objects that store arbitrary keys, especially if those keys may collide with names like constructor or toString. When you choose a null prototype, you trade convenience methods for a cleaner storage object, so plan on using Object.hasOwn() and similar static helpers instead of instance methods.
Object.create(null)
Passing null as the prototype creates a “dictionary” object — a plain key-value store with no inherited properties from Object.prototype. This is useful for safe caches and maps where the keys might collide with inherited names like toString, constructor, or hasOwnProperty. Such objects cannot use methods like obj.hasOwnProperty() directly (there is none to inherit), so use Object.hasOwn() instead.
const dict = Object.create(null);
dict.constructor = 'a string'; // safe — no conflict with Object.prototype.constructor
Object.hasOwn(dict, 'constructor'); // true
Property descriptors in the second argument
The optional second argument to Object.create() takes property descriptors in the same format as Object.defineProperties(). Each key maps to a descriptor object with fields like value, writable, enumerable, and configurable. Properties defined this way are non-enumerable by default (unlike properties assigned with =), which means they will not appear in for...in loops or Object.keys(). This makes Object.create() useful when you need to attach hidden metadata to an object without polluting its enumerable surface.
See Also
- Object.setPrototypeOf() — Set prototype of object
- Object.getPrototypeOf() — Get prototype of object
- Object.assign() — Assign properties
- Object.freeze() — MDN documentation