Object.create()

Classes replaced it for inheritance. What survives is Object.create(null) — the way to build a dictionary that cannot collide with anything on Object.prototype.

Object static methodES5 (2009)Live demo
Common call
Object.create(null)
Returns
a new object with the given prototype
Replaces
the constructor-and-prototype dance, for inheritance
Watch out
a null-prototype object has no toString or hasOwnProperty
Object.create(proto[, descriptors])
→ object

Demo

Live evaluation
Try:
Inputs
jsonstringa JSON object to use as prototype
Output
(o => [Object.keys(o), o.x])(Object.create(JSON.parse('{"x":1}')))
[[], 1]

The pair shows the new object own keys and the value it reads for x. In the first case the keys array is EMPTY while o.x is 1 — the value is found on the prototype, not on the object itself. That is the entire mechanism: create gives you a blank object that delegates lookups upward. It is also why Object.keys, Object.entries and JSON.stringify all see nothing here, while a plain property read succeeds. When the prototype has no x at all, the read is undefined rather than an error.

Parameters

NameTypeRequiredDescription
protoobject | nullyesThe prototype for the new object. null creates an object with no prototype chain whatsoever. Any other primitive throws TypeError.
descriptorsobjectno (none)Property descriptors in the same shape Object.defineProperties takes — so properties added here are non-enumerable by default, exactly as with defineProperty.

Return value

object — A new object with NO own properties, whose prototype is the object you passed. Passing null gives an object that inherits nothing at all.

Common patterns

A dictionary with no inherited keys
The one use that modern code still needs.
const counts = Object.create(null);
counts[userKey] = 1;
Copy an object with its prototype
Where spread would flatten to a plain object.
Object.assign(Object.create(Object.getPrototypeOf(src)), src);
Use a class for inheritance
What create used to be for.
class Dog extends Animal { }

Examples

1. No own properties
Object.keys(Object.create({x: 1}))
Returns
[]
2. But x is readable
Object.create({x: 1}).x
Returns
1
3. Null prototype
Object.getPrototypeOf(Object.create(null))
Returns
null
4. No inherited methods
"toString" in Object.create(null)
Returns
false
5. So coercion throws
`${Object.create(null)}`
Returns
TypeError: Cannot convert object to primitive value
6. With descriptors
Object.create(null, {a: {value: 1, enumerable: true}})
Returns
{a: 1}

Pitfalls

1. A null-prototype object has no methods at all
No toString, no hasOwnProperty, no valueOf. String conversion throws rather than producing "[object Object]", so a stray template literal or a console.log in some libraries will fail on it.
Coercion throws
`${Object.create(null)}`
TypeError: Cannot convert object to primitive value
Use the statics
JSON.stringify(dict);
Object.hasOwn(dict, k);
work regardless of prototype
2. Properties added via descriptors are non-enumerable
The second argument is Object.defineProperties, not an object literal, so it inherits the same default of enumerable: false. Properties you add this way silently vanish from Object.keys and JSON.stringify.
Invisible
Object.keys(Object.create(null, {a: {value: 1}}))
[]
Say enumerable
Object.keys(Object.create(null, {a: {value: 1, enumerable: true}}))
['a']
3. It is not a copy
Object.create(src) does not clone src — it creates an empty object that DELEGATES to it. Changing the original afterwards changes what the new object appears to contain, and writing to the new object shadows rather than updates.
Linked, not copied
const src = {x: 1};
const o = Object.create(src);
src.x = 9;
o.x
9
Actually copy
const o = {...src};
src.x = 9;
o.x
1
4. A primitive prototype throws
Only an object or null is accepted. Passing a number or a string — easy when the value came from data — is a TypeError rather than a silent coercion.
Throws
Object.create(42)
TypeError: Object prototype may only be an Object or null: 42
Guard it
Object.create(typeof p === "object" ? p : null)
safe

When to use

Use it
  • Dictionaries keyed by untrusted strings — Object.create(null)
  • Copying an object while preserving its prototype
  • Setting up a prototype chain without a constructor
  • Creating an object with precisely specified descriptors
Reach for something else
  • Class-style inheritance → class and extends
  • You want a copy → spread, Object.assign or structuredClone
  • A keyed collection → a Map, which needs no prototype tricks
  • The result will be string-coerced → a null prototype will throw

Notes

Complexity
O(1), or O(n) with a descriptors argument
Return
A new object; no properties are copied from the prototype
CPython impl
V8: Builtins-object-create
Memory
Allocates one empty object
Thread-safe
Single-threaded

FAQ

Because a plain object inherits from Object.prototype, so keys like "toString" or "constructor" appear to exist before you set them, and a key of "__proto__" can change the prototype instead of storing a value. A null-prototype object has nothing to collide with. A Map avoids the problem entirely and is usually the better answer.

const d = Object.create(null);
d.toString;   // undefined, not a function

History

ES5
Object.create added, giving direct control over prototypes for the first time.
ES2015
class syntax arrived and took over inheritance, leaving the null-prototype case as the main remaining use.