Skip to main content

Command Palette

Search for a command to run...

JavaScript Prototypes

Updated
3 min read

Ever caught yourself typing something like [1,2,3].map() and suddenly wondered, where the hell does map actually live? You never defined it. It's not sitting there in your array. But it works anyway, every single time.

Here's the part they don't teach clearly every object in JavaScript has a secret connection to another object. Behind the scenes, there's an internal link called [[Prototype]]. You can't touch it directly, but you can peek at it.

const user = { name: "Chirag" };
console.log(Object.getPrototypeOf(user));

There's also this old, informal way. The point is thi when JavaScript looks for a property on user and doesn't find it, it doesn't give up. It follows that hidden link upward. It checks the next object. And the next. This chain lookup is the engine that makes JavaScript inheritance work.

Following the Chain

Let's trace what actually happens when you run user.toString().

First, JavaScript checks user itself. No toString there. So it hops up to user.__proto__, which points to Object.prototype. toString lives there. If it hadn't found it? It would keep climbing. The chain goes: user - Object.prototype -null. null means stop. End of the road. If the property isn't found by then, you get undefined.

The Part Where Functions Get Weird

Okay, here's where people usually get lost. Functions in JavaScript have a prototype property. But that's not the same as __proto__.

function User(name) {
  this.name = name;
}

User.prototype.sayHello = function () {
  return `Hello ${this.name}`;
};

const u1 = new User("Arpit");
console.log(u1.sayHello()); 

// "Hello Arpit"

What does new actually do? Four things creates an empty object, links its __proto__ to User.prototype, binds this to that new object, and returns it. That's it. The User.prototype object becomes the prototype for every instance you create. So u1 doesn't carry sayHello around in its pocket. It borrows it from User.prototype. One method, shared across every user. That's the memory efficiency people talk about.

ES6 gave us the class keyword, and it looks clean -

class User {
  constructor(name) {
    this.name = name;
  }

  sayHello() {
    return `Hello ${this.name}`;
  }
}

Why Any of This Actually Matters

In real systems, prototypes aren't academic trivia. They're practical tools with real consequences.

Memory efficiency first - When you put methods on a prototype, every instance shares one function object. Put them in the constructor with this.method and you're duplicating that function for every single instance. At scale this is what matters(a lot )

Performance next - Deep prototype chains slow down property lookup. If your object inherits from something that inherits from something else, JavaScript has to climb more links. Usually negligible, but worth knowing if you're optimizing hot paths.

The Takeaway

Prototypes aren't some dusty JavaScript quirk. They're the living mechanism behind every method call, every inheritance chain, every class you write. Objects delegate upward. The chain ends at null. Functions have a prototype property that becomes the __proto__ of their instances.