We can do console.log(Object.prototype) → and it shows the properties and methods that all objects inherit.But try console.log (Object.__proto__) → it gives ƒ () { [native code] }.
Ever wondered why?
Because our initial object is created using a constructor.
And only in constructors we can add properties by using prototype.
Now, who inherits?
It’s like our global Object constructor. The objects created from it inherit through prototype.
That prototype itself has inner things we use—like .create() and .__proto__.
All these are functions living on Object.prototype.
lets deep dive
1 — Quick overview
Every object in JavaScript has a hidden pointer to another object (its prototype). When you ask for a property, JS checks the object first, then follows that pointer up the chain until it finds the property or reaches null.
2 — Object.create(proto) example (start here)
const parent = {
greet() { console.log("Hello from parent!"); }
};
const child = Object.create(parent);
console.log(child); // shows child as {}
console.log(child.__proto__); // shows the parent object in console
child.greet(); // "Hello from parent!"
What happened (step by step):
-
childis created as an empty object. - Internally, JS sets
child’s hidden slot[[Prototype]]to point toparent. (DevTools shows this aschild.__proto__.) - When you call
child.greet(), JS doesn’t findgreetonchild, so it looks atchild.__proto__(theparent) and findsgreetthere.
ASCII view:
child {}
__proto__ → parent { greet: f }
__proto__ → Object.prototype
__proto__ → null
3 — Understanding the console output: { greet: ƒ } [[Prototype]]: Object
When the console shows:
{ greet: ƒ }
[[Prototype]]: Object
-
{ greet: ƒ }= theparentobject (it has agreetfunction). -
[[Prototype]]: Object= meaning theparentobject itself has a prototype, which isObject.prototype. - The double brackets
[[Prototype]]mean this is an internal slot (not a normal enumerable property).
Simple sentence: the console is telling you the parent has its own parent — Object.prototype.
4 — __proto__ vs prototype (exact difference)
-
__proto__- Exists on objects.
- It’s the actual reference (internal slot) to the parent object.
- You can inspect it with
obj.__proto__orObject.getPrototypeOf(obj).
-
prototype- Exists on constructor functions (e.g.,
function Person(){}→Person.prototype). - It is the object that will become the
__proto__of instances created by that constructor.
- Exists on constructor functions (e.g.,
Proof code:
function Person(){ }
const p = new Person();
console.log(p.__proto__ === Person.prototype); // true
5 — What new does (exact, ordered steps)
When you run const x = new C(args):
- Create a new empty object:
{}. - Set the object’s internal
[[Prototype]]toC.prototype. - Call
Cwiththisset to that new object, filling properties onthis. - Return the new object (unless constructor explicitly returns another object).
Visual:
x { own properties }
__proto__ → C.prototype { shared methods }
__proto__ → Object.prototype
__proto__ → null
6 — Property lookup algorithm (how JS finds a property)
When you do obj.someProp:
- Check
obj’s own properties. If found → return it. - Else, set
obj = obj.__proto__and repeat step 1. - Continue until
objbecomesnull. If not found →undefined.
This is why child.greet() works even though greet is not on child: the lookup finds greet on parent.
7 — Where to put data vs methods (practical rule)
- Put per-object data inside the constructor (each instance gets its own copy).
function Person(name){ this.name = name; }
- Put shared methods on the constructor’s
prototype(all instances reuse the same function).
Person.prototype.sayHi = function(){ console.log(this.name); }
Why: methods on prototype save memory and make behavior consistent for all instances.
8 — The top of the chain: Object.prototype → null
- Most objects eventually point to
Object.prototype. -
Object.prototype.__proto__isnull. Thatnullis the end — no more lookup beyond that.
Example check:
console.log(Object.getPrototypeOf({}) === Object.prototype); // true
console.log(Object.getPrototypeOf(Object.prototype)); // null
9 — Function vs Object (short note)
-
Objectitself is a function (typeof Object === 'function'). - Functions are objects too: they have their own
__proto__(usuallyFunction.prototype). - So constructors and built-ins follow the same prototype rules (they are objects that link to prototypes).
10 — How to inspect safely (recommended tools)
-
Object.getPrototypeOf(obj)→ preferred way to get prototype (safer than__proto__). -
obj.hasOwnProperty('x')→ checks own properties only. -
console.log(obj.__proto__)or expand object in DevTools → shows[[Prototype]].
11 — Common mistakes & best practices
-
Don’t rely on
__proto__for setting prototypes in production (it’s slower). UseObject.create(proto)orObject.setPrototypeOf()only when needed. -
Don’t mutate shared prototype state carelessly — changing
PrototypeObject.sharedArray.push(...)affects all instances. - Use
prototypefor methods, constructor for instance data.
12 — Short cheat-sheet (memorize this)
-
Object.create(proto)→ create object whose__proto__isproto. -
__proto__→ object’s hidden parent pointer. -
prototype→ constructor’s blueprint object. -
new→ links instance__proto__to constructor’sprototype, then runs constructor. -
Object.prototype.__proto__ === null→ chain end.
Top comments (2)
Thanks for posting this! A lot of people have confusion around the prototype chain because it isn't discussed or used as much these days, and the class structure layered on top of it can make it even harder to understand.
One note: In the "How to inspect safely" section you use
obj.hasOwnPropertyto check for a property, but that's not really safe, as the prototype can overridehasOwnProperty! Object.hasOwn(obj, propertyName) is the recommended safe solution for that.thanks for telling @oculus42 ☺