DEV Community

yashi srivastava
yashi srivastava

Posted on

JavaScript revision blew my mind today.

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!"
Enter fullscreen mode Exit fullscreen mode

What happened (step by step):

  1. child is created as an empty object.
  2. Internally, JS sets child’s hidden slot [[Prototype]] to point to parent. (DevTools shows this as child.__proto__.)
  3. When you call child.greet(), JS doesn’t find greet on child, so it looks at child.__proto__ (the parent) and finds greet there.

ASCII view:

child {} 
   __proto__ → parent { greet: f }
                   __proto__ → Object.prototype
                                      __proto__ → null
Enter fullscreen mode Exit fullscreen mode

3 — Understanding the console output: { greet: ƒ } [[Prototype]]: Object

When the console shows:

{ greet: ƒ }
[[Prototype]]: Object
Enter fullscreen mode Exit fullscreen mode
  • { greet: ƒ } = the parent object (it has a greet function).
  • [[Prototype]]: Object = meaning the parent object itself has a prototype, which is Object.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__ or Object.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.

Proof code:

function Person(){ }
const p = new Person();
console.log(p.__proto__ === Person.prototype); // true
Enter fullscreen mode Exit fullscreen mode

5 — What new does (exact, ordered steps)

When you run const x = new C(args):

  1. Create a new empty object: {}.
  2. Set the object’s internal [[Prototype]] to C.prototype.
  3. Call C with this set to that new object, filling properties on this.
  4. Return the new object (unless constructor explicitly returns another object).

Visual:

x { own properties }
   __proto__ → C.prototype { shared methods }
                   __proto__ → Object.prototype
                                      __proto__ → null
Enter fullscreen mode Exit fullscreen mode

6 — Property lookup algorithm (how JS finds a property)

When you do obj.someProp:

  1. Check obj’s own properties. If found → return it.
  2. Else, set obj = obj.__proto__ and repeat step 1.
  3. Continue until obj becomes null. 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; }
Enter fullscreen mode Exit fullscreen mode
  • Put shared methods on the constructor’s prototype (all instances reuse the same function).
  Person.prototype.sayHi = function(){ console.log(this.name); }
Enter fullscreen mode Exit fullscreen mode

Why: methods on prototype save memory and make behavior consistent for all instances.


8 — The top of the chain: Object.prototypenull

  • Most objects eventually point to Object.prototype.
  • Object.prototype.__proto__ is null. That null is the end — no more lookup beyond that.

Example check:

console.log(Object.getPrototypeOf({}) === Object.prototype); // true
console.log(Object.getPrototypeOf(Object.prototype)); // null
Enter fullscreen mode Exit fullscreen mode

9 — Function vs Object (short note)

  • Object itself is a function (typeof Object === 'function').
  • Functions are objects too: they have their own __proto__ (usually Function.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). Use Object.create(proto) or Object.setPrototypeOf() only when needed.
  • Don’t mutate shared prototype state carelessly — changing PrototypeObject.sharedArray.push(...) affects all instances.
  • Use prototype for methods, constructor for instance data.

12 — Short cheat-sheet (memorize this)

  • Object.create(proto) → create object whose __proto__ is proto.
  • __proto__ → object’s hidden parent pointer.
  • prototype → constructor’s blueprint object.
  • new → links instance __proto__ to constructor’s prototype, then runs constructor.
  • Object.prototype.__proto__ === null → chain end.

Top comments (0)