Prototypal Inheritance
debt(d5/e3/b7/t8)
Closest to 'specialist tool catches it' (d5). ESLint and TypeScript can flag some anti-patterns (direct __proto__ access, native prototype pollution), but detecting misunderstandings of the prototype chain itself—the core misconception—requires code review or runtime testing. Most prototype-related bugs are silent until property lookup fails or performance degrades.
Closest to 'simple parameterised fix' (e3). The quick_fix explicitly states 'use class syntax instead of direct prototype manipulation'—a straightforward replacement within a single constructor/method. Refactoring methods from constructor body to prototype.methodName is a contained, local change. No cross-cutting rework needed.
Closest to 'strong gravitational pull' (b7). Prototypal inheritance is a foundational mechanism in JavaScript; every object inherits via the prototype chain. Decisions about how to structure inheritance (class vs. direct prototype manipulation, where to define methods) shape how the entire codebase organizes objects and their lifecycles. Misuse pollutes global scope (native prototype extensions) and affects all downstream code. It applies to both web and CLI contexts.
Closest to 'serious trap' (t7). The canonical misconception—'ES6 classes replaced prototype-based inheritance'—directly contradicts JavaScript's actual design. Developers familiar with classical OOP (Java, Python) expect classes to be a new inheritance model, not sugar. This leads to the common mistakes listed: confusing __proto__ with prototype, creating methods in constructors, extending native prototypes. The 'obvious' syntactic difference (class vs. function constructor) masks identical underlying mechanics, causing hidden bugs.
Also Known As
TL;DR
Explanation
Every JavaScript object has an internal [[Prototype]] link to another object (or null). Property lookups traverse the chain: if a property isn't on the object, JS checks its prototype, then the prototype's prototype, up to Object.prototype. ES6 class syntax creates the same prototype chain but with readable syntax — class Dog extends Animal {} sets Dog.prototype's [[Prototype]] to Animal.prototype. Key differences from classical inheritance: prototypes are live objects (modifications affect all instances), methods are shared on the prototype not copied per instance, and Object.create() enables prototype-based patterns without class syntax. PHP's class-based inheritance is classical — understanding JS prototypes helps when debugging constructor functions and instanceof checks.
Common Misconception
Why It Matters
Common Mistakes
- Extending native prototypes (Array.prototype.myMethod) — pollutes the global namespace and breaks for-in loops.
- Confusing __proto__ (instance's prototype link) with prototype (constructor's prototype property).
- Creating methods inside the constructor function body — creates a new function object per instance; define on prototype.
- Not understanding that class syntax is syntactic sugar over prototype chains — same engine mechanics.
Code Examples
// Methods in constructor — new function per instance, wastes memory:
function User(name) {
this.name = name;
this.greet = function() { return 'Hi, ' + this.name; }; // New function for every User
}
// Prototype method — shared across all instances:
User.prototype.greet = function() { return 'Hi, ' + this.name; };
// Or with class syntax (equivalent under the hood):
class User { greet() { return 'Hi, ' + this.name; } }
// ES6 class — syntactic sugar over prototype chain
class Animal {
constructor(name) { this.name = name; }
speak() { return `${this.name} makes a noise.`; }
}
class Dog extends Animal {
speak() { return `${this.name} barks.`; }
}
const d = new Dog('Rex');
console.log(d.speak()); // Rex barks.
console.log(d instanceof Animal); // true
// Inspect the prototype chain:
console.log(Object.getPrototypeOf(d) === Dog.prototype); // true
console.log(Object.getPrototypeOf(Dog.prototype) === Animal.prototype); // true
// Mixin pattern — share behaviour without inheritance
const Serializable = (Base) => class extends Base {
toJson() { return JSON.stringify(this); }
};
class SerializableDog extends Serializable(Dog) {}