Prototypes & the this Keyword
22 min read
Prototype Chain__proto__ vs prototypethis Keywordcall apply bindArrow FunctionsClassesPrivate Fields
Prototypal Inheritance
JavaScript doesn't use classical inheritance. Instead, objects delegate to other objects via the prototype chain.
The Prototype Chain
const animal = { speak() { return "..."; } };
const dog = Object.create(animal);
dog.bark = function() { return "Woof!"; };
dog.bark(); // "Woof!" (own property)
dog.speak(); // "..." (delegated to animal via prototype)
dog.toString(); // "[object Object]" (delegated to Object.prototype)__proto__ vs prototype
| Property | Exists On | Purpose |
|---|---|---|
__proto__ | Every object | Points to the object's prototype (the parent) |
prototype | Functions only | The object that will become __proto__ for instances |
function Person(name) { this.name = name; }
Person.prototype.greet = function() { return `Hi, I'm ${this.name}`; };
const alice = new Person("Alice");
alice.__proto__ === Person.prototype; // true
Person.prototype.__proto__ === Object.prototype; // true
Object.prototype.__proto__ === null; // END of chainThe this Keyword
this is determined by HOW a function is called, not where it's defined.
| Call Style | this Value |
|---|---|
func() | window (or undefined in strict mode) |
obj.func() | obj |
new Func() | New empty object |
func.call(ctx) | ctx |
func.bind(ctx)() | ctx (permanently) |
() => {} | Lexical (inherits from parent scope) |
Arrow Functions & this
const obj = {
name: "Alice",
// ❌ Arrow function inherits `this` from outer scope (window/module)
greetArrow: () => console.log(this.name),
// ✅ Regular function gets `this` = obj
greetRegular() { console.log(this.name); }
};
obj.greetArrow(); // undefined
obj.greetRegular(); // "Alice"call, apply, bind
function introduce(greeting) {
return `${greeting}, I'm ${this.name}`;
}
const user = { name: "Bob" };
introduce.call(user, "Hello"); // "Hello, I'm Bob"
introduce.apply(user, ["Hello"]); // "Hello, I'm Bob"
const bound = introduce.bind(user);
bound("Hey"); // "Hey, I'm Bob"Classes (Syntactic Sugar)
class Animal {
#sound; // Private field
constructor(name, sound) {
this.name = name;
this.#sound = sound;
}
speak() { return `${this.name} says ${this.#sound}`; }
static create(name, sound) { return new Animal(name, sound); }
}
class Dog extends Animal {
constructor(name) { super(name, "Woof"); }
fetch(item) { return `${this.name} fetches ${item}`; }
}
const rex = Dog.create("Rex", "Woof"); // Error: creates Animal, not Dog
const rex2 = new Dog("Rex");
rex2.speak(); // "Rex says Woof"