Object.create and the prototype chain
Build objects with Object.create, walk [[Prototype]], distinguish own vs inherited props, and know null-prototype maps.
- javascript
- object-create
Every ordinary object has an internal [[Prototype]] link. Property lookup walks that chain until it finds a name or hits null. Object.create(proto) builds a new object whose prototype is exactly proto.
const animal = {
eat() { return `${this.name} eats`; },
};
const dog = Object.create(animal);
dog.name = 'Rex';
dog.eat(); // 'Rex eats' — method found on prototype, this is dog
Own vs inherited
const base = { a: 1 };
const child = Object.create(base);
child.b = 2;
child.hasOwnProperty('a'); // false
child.hasOwnProperty('b'); // true
'a' in child; // true (walks chain)
Object.keys(child); // ['b'] — own enumerable only
| Check | Own only | Chain |
|---|---|---|
obj.prop / in |
✓ | |
Object.keys / hasOwn |
✓ | |
for...in |
enumerable on chain | ✓ |
Prefer Object.hasOwn(obj, key) (or Object.prototype.hasOwnProperty.call) over assuming hasOwnProperty exists — objects can shadow it.
Object.create signature
Object.create(proto, propertiesObject?);
const point = Object.create(Object.prototype, {
x: { value: 0, writable: true, enumerable: true, configurable: true },
y: { value: 0, writable: true, enumerable: true, configurable: true },
});
Second arg is property descriptors — same shape as Object.defineProperties.
Null-prototype objects
const map = Object.create(null);
map.__proto__; // undefined — no Object.prototype
map.toString; // undefined
// Safe dictionary: keys won’t clash with 'toString', 'constructor', etc.
map['toString'] = 'safe';
Useful for maps keyed by arbitrary strings (user input). Tradeoff: no hasOwnProperty method — use Object.hasOwn(map, k).
__proto__ vs Object.getPrototypeOf
Object.getPrototypeOf(dog) === animal; // true
Object.setPrototypeOf(dog, other); // possible, usually slow — avoid hot paths
obj.__proto__ is legacy accessor on Object.prototype; don’t use it in new code. Setting prototypes after creation deopts engines.
vs class / constructor
function Dog(name) {
this.name = name;
}
Dog.prototype.bark = function () { return 'woof'; };
const d = new Dog('Rex');
Object.getPrototypeOf(d) === Dog.prototype;
new sets [[Prototype]] to Constructor.prototype. Object.create(Dog.prototype) without new skips constructor body — pattern for subclassing in old code:
function Terrier(name) {
Dog.call(this, name);
}
Terrier.prototype = Object.create(Dog.prototype);
Terrier.prototype.constructor = Terrier;
Today: class Terrier extends Dog.
Interview answer (out loud)
“Property lookup walks the prototype chain.
Object.create(proto)makes an object with that prototype. Own properties sit on the object; inherited ones live up the chain.Object.create(null)makes a pure dictionary without Object.prototype. Prefer get/setPrototypeOf and hasOwn over proto hacks.”
Checking the chain
function getProtoChain(obj) {
const chain = [];
let cur = obj;
while (cur) {
chain.push(cur);
cur = Object.getPrototypeOf(cur);
}
return chain; // ends before null
}
Object.getPrototypeOf(Object.prototype); // null
Property shadowing
const p = { x: 1 };
const c = Object.create(p);
c.x = 2; // own property shadows
delete c.x;
c.x; // 1 again from prototype
Assignment usually creates an own data property (unless an accessor on the chain traps it). Understanding shadowing explains unexpected delete behavior.
Further reading
Related
Related guides
- AbortControllerCancel fetch and other async work with AbortController and AbortSignal — timeouts, race conditions, and cleanup when components unmount.
- Array find, some, every, includesShort-circuiting array predicates: find, findIndex, some, every, and includes — when to use each and common interview traps.
- Array map, filter, reducemap, filter, and reduce as the core transform toolkit — immutability, chaining costs, reduce patterns, and when a plain loop is clearer.
- Arrow Functions Deep DiveLexical this, no arguments object, no construct, concise bodies — when arrows help and when methods and generators need classic functions.
- Async Iteration and for await...ofAsync iterables, for await...of, and streaming data — how async iterators differ from Promise.all and when to use each.