The Secret Life of JavaScript: Understanding Prototypes
Source: Dev.to
Objects Delegate, They Don’t Copy
Timothy arrived at the library with his laptop and a puzzled expression. He’d been reading about JavaScript inheritance, and something didn’t add up.
Timothy: “I learned that JavaScript uses prototypes for inheritance. But I’ve also learned that JavaScript has classes now. So which is it? Does JavaScript use prototypes or classes?”
Margaret: “Both. And neither. It’s more subtle than that.”
What is inheritance?
Timothy: “When a child class inherits from a parent class, it gets all the parent’s properties and methods.”
Margaret: “In languages like Python and Java, yes. The child class copies or includes the parent’s behavior. JavaScript, however, uses delegation.”
Delegation vs. copying
Instead of copying properties down, objects in JavaScript point to other objects and say: “If I don’t have what you’re looking for, ask that object over there.”
Simple example
const parent = {
greet: function() {
console.log("Hello, " + this.name);
}
};
const child = Object.create(parent);
child.name = "Alice";
child.greet(); // "Hello, Alice"
Object.create(parent)creates a new object whose hidden[[Prototype]]link points toparent.- When
child.greet()is called, JavaScript looks forgreetonchild. Not found → looks onchild’s prototype (parent). Finds it and calls it withthisbound tochild.
Multiple objects sharing the same prototype
const parent = {
greet: function() {
console.log("Hello, " + this.name);
}
};
const alice = Object.create(parent);
alice.name = "Alice";
const bob = Object.create(parent);
bob.name = "Bob";
alice.greet(); // "Hello, Alice"
bob.greet(); // "Hello, Bob"
Both alice and bob delegate to the same parent object, but this inside greet refers to the caller.
[[Prototype]] vs. .prototype
The hidden link [[Prototype]]
const obj = Object.create(parent);
// obj.[[Prototype]] points to parent (invisible)
The .prototype property on functions
function User(name) {
this.name = name;
}
// User.prototype is a regular property on the User function
console.log(User.prototype); // { constructor: User }
Using new
function User(name) {
this.name = name;
}
User.prototype.greet = function() {
console.log("Hello, " + this.name);
};
const alice = new User("Alice");
alice.greet(); // "Hello, Alice"
When new User("Alice") runs, JavaScript:
- Creates a brand‑new object.
- Sets that object’s
[[Prototype]]toUser.prototype. - Calls
Userwiththisbound to the new object. - Returns the new object.
You can verify the link:
console.log(Object.getPrototypeOf(alice) === User.prototype); // true
Object.getPrototypeOf() (or the legacy __proto__) reveals the invisible prototype chain.
Classes: Syntactic Sugar Over Prototypes
Class version of the previous example
class User {
constructor(name) {
this.name = name;
}
greet() {
console.log("Hello, " + this.name);
}
}
const alice = new User("Alice");
alice.greet(); // "Hello, Alice"
- The class syntax creates the same prototype chain under the hood.
- Methods defined inside a class (
greet) are placed onUser.prototype. new User()still creates an object whose[[Prototype]]points toUser.prototype.
Why “syntactic sugar”?
The term means the syntax looks different and is often clearer, but the underlying mechanism—prototype delegation—remains unchanged. Classes also provide convenient keywords like extends and super(), which handle prototype chaining for you, but they do not introduce a new inheritance model.
Summary
- JavaScript objects delegate to other objects via the hidden
[[Prototype]]link. - The
.prototypeproperty on constructor functions is a way to set up that hidden link for objects created withnew. Object.getPrototypeOf()(or__proto__) lets you inspect the prototype chain.- ES6 classes are merely a cleaner way to write the same prototype‑based pattern; they do not replace prototypes.