ES6 Classes
A class is syntax on top of a constructor function and its prototype, not a new kind of object.
Loading visual…
Definition
The `class` keyword, introduced in ES6, is syntactic sugar over the constructor function and prototype pattern JavaScript already had. Writing `class Dog { constructor(name) { this.name = name; } bark() { return this.name + " barks"; } }` produces the same shape under the hood as a `function Dog(name)` with `this.name = name`, plus a `bark` method assigned to `Dog.prototype`. Every method written inside the class body, other than the constructor, is placed on the prototype automatically, so all instances share one copy instead of getting their own. Calling `new Dog("Rex")` creates a new plain object, sets its internal prototype to `Dog.prototype`, runs the constructor with `this` bound to that new object, and returns it. `class Puppy extends Dog` builds `Puppy.prototype` with `Dog.prototype` as its own prototype, chaining the two, and `super(name)` inside `Puppy`'s constructor calls `Dog`'s constructor first. Classes add conveniences the older pattern does not have for free, like a body that throws if called without `new`, and methods that are non-enumerable by default, but the underlying mechanism is the same prototype chain.
Examples
class Dog {
constructor(name) {
this.name = name;
}
bark() {
return this.name + " barks";
}
}
const rex = new Dog("Rex");
rex.bark(); // "Rex barks"
typeof Dog.prototype.bark; // "function"bark is defined in the class body but ends up on Dog.prototype, exactly where a hand-written prototype method would live.
class Animal {
constructor(name) { this.name = name; }
speak() { return this.name + " makes a sound"; }
}
class Cat extends Animal {
speak() { return this.name + " meows"; }
}
new Cat("Milo").speak(); // "Milo meows"extends links Cat.prototype to Animal.prototype, and Cat's own speak on its prototype shadows Animal's version for Cat instances.
class Counter {
static instances = 0;
constructor() {
Counter.instances++;
}
}
new Counter();
new Counter();
Counter.instances; // 2static fields and methods live on the class itself, Counter, rather than on Counter.prototype or on individual instances.
Where you see this
- Modeling domain objects like ShoppingCart or BankAccount with clear constructors and shared methods.
- Building inheritance hierarchies, like a base Shape class with Square and Rect subclasses overriding area().
- Reading unfamiliar codebases that mix class syntax with older constructor-function code, knowing they compile to the same thing.
- Using static methods for utilities that belong to the class itself rather than to any one instance.