Prototype vs Class: When to Use Each
Class syntax sits on top of prototypes. Knowing both helps you debug and design APIs.
What Prototype Actually Is
Every object has an internal [[Prototype]] slot pointing to another object. Property lookup walks the chain:
``js
const animal = { eats: true };
const dog = Object.create(animal);
dog.barks = true;
dog.eats; // true (from prototype)
`
Class Syntax Translation
`js
class Dog {
constructor(name) { this.name = name; }
bark() { return 'woof'; }
}
// Equivalent to:
function Dog(name) { this.name = name; }
Dog.prototype.bark = function() { return 'woof'; };
`
Differences That Bite
- Hoisting: function declarations hoist; class declarations are in the temporal dead zone
- Strict mode: class bodies are always strict
- Constructor invocation: classes throw without new; functions silently bind this to global
- Methods are non-enumerable in classes, enumerable when assigned to prototype manually
When to Use Plain Objects
For simple data with no behavior, plain objects beat classes. JIT compilers optimize them well, and serialization is trivial.
When to Use Classes
- Multiple instances sharing methods - Inheritance hierarchies (rare but valid) - Frameworks expecting class syntax (React class components, Web Components)
When to Use Closures Instead
Closures provide true privacy without #field` syntax and play better with composition. See [closures real-world examples](/blog/closures-real-world-examples) for patterns.
Modern Compromise
For most app code, use closure-based factory functions. Reserve classes for genuinely OO domains like Web Components.