Object.getPrototypeOf()
ES5+Returns the internal prototype ([[Prototype]]) of an object — the object it delegates to when a property lookup misses — or null at the end of the chain. It is the standards-blessed replacement for the legacy __proto__ accessor.
Syntax
Object.getPrototypeOf(obj)Parameters
obj Object The object whose prototype is to be returned
Return Value
The prototype of the given object
Examples
const arr = [1, 2, 3];
console.log(Object.getPrototypeOf(arr) === Array.prototype); 📌 When to Use
Use Object.getPrototypeOf() when you need to inspect or reason about the delegation chain itself rather than the object's own data. Concrete cases: verifying inheritance wiring in tests (Object.getPrototypeOf(dog) === Dog.prototype proves the setup without invoking behavior), writing cloning utilities that must reproduce an object's prototype (pair it with Object.create()), implementing framework internals that walk the chain to collect metadata from every ancestor level, and debugging "where does this method actually come from?" questions by hopping the chain level by level. It also matters for cross-realm checks — an array from an iframe fails arr instanceof Array in the parent frame because each realm has its own prototypes, which is exactly why Array.isArray() exists; getPrototypeOf lets you build similarly deliberate, realm-aware checks when needed. For everyday type checking inside one realm, instanceof remains clearer and handles the whole chain automatically. Finally, prefer reading prototypes to writing them: its counterpart Object.setPrototypeOf() deoptimizes objects in most engines, so chains should be established at creation time and merely inspected afterward.
⚠️ Common Mistakes
Comparing prototypes across execution contexts - each iframe, worker, or Node vm has its own copy of every built-in prototype, so Object.getPrototypeOf(valueFromIframe) === Array.prototype is false even for a genuine array. Use Array.isArray() or structural checks when values can cross realm boundaries, and reserve prototype identity comparison for code that provably stays within one realm.
Mutating the returned prototype object as if it were a private copy - getPrototypeOf hands back the live shared prototype. Assigning onto it (proto.helper = fn) instantly affects every object that inherits from it, which is how accidental global monkey-patches happen.
Using it on arbitrary values without a guard - Object.getPrototypeOf("hi") returns String.prototype because the primitive is wrapped, while Object.getPrototypeOf(null) and Object.getPrototypeOf(undefined) throw a TypeError. Code inspecting unknown values needs a typeof and null check before the call.
Using __proto__ instead of Object.getPrototypeOf() - __proto__ is deprecated and should not be used in modern code.
Expecting getPrototypeOf to return the constructor - it returns the prototype object, not the constructor function.
Not handling null prototype - objects created with Object.create(null) return null, not an empty object.
✅ Best Practices
Preserve prototypes when cloning: Object.create(Object.getPrototypeOf(src), Object.getOwnPropertyDescriptors(src)) keeps both the chain and accessor properties intact - spread syntax always produces a plain Object.prototype object and silently discards the original prototype.
Read prototypes freely but never rewrite them on live objects - if you feel the need for Object.setPrototypeOf(), restructure so objects are created with the right prototype from the start via Object.create() or class hierarchies; post-creation prototype changes permanently deoptimize the object in most engines.
Use for instanceof-like checks that work across frames: Object.getPrototypeOf(obj) === Array.prototype
Create prototype chain inspection utilities for debugging: function getPrototypeChain(obj) { ... }
Prefer instanceof for type checking in most cases - it is more readable and handles the prototype chain automatically.
⚡ Performance Notes
Reading a prototype with Object.getPrototypeOf() is an O(1) internal-slot read — effectively free anywhere, including hot paths. The performance story worth knowing is the asymmetry with writing: engines like V8 attach optimization data (hidden classes, inline caches) to the assumption that an object's prototype never changes after creation, so Object.setPrototypeOf() or __proto__ assignment invalidates those caches and can permanently deoptimize every subsequent access to the mutated object. Chain-walking utilities that loop getPrototypeOf until null do O(depth) work; real chains are short (two to four links), so this is cheap, but memoize results if a reflection-heavy framework walks the same constructors repeatedly. Property lookups themselves get slower the deeper a miss must travel up the chain, which is one reason flat object designs and null-prototype dictionaries probe faster on missing keys.
🌍 Real World Example
Prototype Chain Inspector
The getPrototypeChain() utility repeatedly applies Object.getPrototypeOf() until it reaches null, labeling each level with its constructor name to make delegation visible. Running it on an array reveals Array, then Object, then the chain end; running it on an instance of a two-level class hierarchy shows Dog, Animal, Object, null. The final assertions confirm the wiring that class syntax creates behind the scenes. Tools like this are invaluable for teaching prototype semantics and for debugging unexpected method resolution in deep hierarchies or mixed-library code.
function getPrototypeChain(obj) {
const chain = [];
let current = obj;
while (current !== null) {
const proto = Object.getPrototypeOf(current);
if (proto === null) {
chain.push({ type: 'null (end of chain)', proto: null });
} else {
const name = proto.constructor?.name || 'Anonymous';
chain.push({ type: name, proto });
}
current = proto;
}
return chain;
}
// Example 1: Array prototype chain
const arr = [1, 2, 3];
const arrayChain = getPrototypeChain(arr);
console.log('Array chain:');
arrayChain.forEach((item, i) => {
console.log(' ' + i + ': ' + item.type);
});
// Array chain:
// 0: Array
// 1: Object
// 2: null (end of chain)
// Example 2: Custom class inheritance
class Animal {
speak() { return 'sound'; }
}
class Dog extends Animal {
bark() { return 'woof'; }
}
const dog = new Dog();
const dogChain = getPrototypeChain(dog);
console.log('Dog chain:');
dogChain.forEach((item, i) => {
console.log(' ' + i + ': ' + item.type);
});
// Dog chain:
// 0: Dog
// 1: Animal
// 2: Object
// 3: null (end of chain)
// Verify prototype relationships
console.log(Object.getPrototypeOf(dog) === Dog.prototype); // true
console.log(Object.getPrototypeOf(Dog.prototype) === Animal.prototype); // true