Learn ES6 class syntax for creating objects, using constructors, methods, getters/setters, static members, and inheritance with extends.
ES6 introduced the class keyword as a cleaner syntax for creating objects with shared behavior. A class is essentially a blueprint for creating objects.
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
greet() {
return `Hi, I'm ${this.name} and I'm ${this.age} years old.`;
}
}
const alice = new Person('Alice', 30);
console.log(alice.greet()); // "Hi, I'm Alice and I'm 30 years old."Key parts:
constructor — a special method called automatically when you create an instance with new. It initializes the object's properties.greet()).new keyword — creates a new instance of the class.Classes are syntactic sugar over JavaScript's existing prototype-based inheritance. Under the hood, class Person still creates a constructor function and adds methods to Person.prototype. But the class syntax is much more readable.
Unlike function declarations, class declarations are NOT hoisted:
const p = new Person('Alice', 30); // ReferenceError!
class Person { ... }You must define the class before using it. This is by design — it prevents subtle bugs caused by hoisting.
Getters and setters let you define computed properties and add validation when setting values. They look like methods but are accessed like properties (no parentheses).
class Circle {
constructor(radius) {
this.radius = radius;
}
// Getter — computed property
get area() {
return Math.PI * this.radius ** 2;
}
get diameter() {
return this.radius * 2;
}
// Setter — validation on assignment
set radius(value) {
if (value < 0) {
throw new RangeError('Radius cannot be negative');
}
this._radius = value;
}
get radius() {
return this._radius;
}
}
const c = new Circle(5);
console.log(c.area); // 78.539... (no parentheses!)
console.log(c.diameter); // 10
c.radius = 10; // uses the setter
// c.radius = -1; // throws RangeErrorES2022 introduced private fields with the # prefix. Private fields are truly inaccessible from outside the class:
class BankAccount {
#balance = 0; // private field
deposit(amount) {
this.#balance += amount;
}
get balance() {
return this.#balance;
}
}
const account = new BankAccount();
account.deposit(100);
console.log(account.balance); // 100
// console.log(account.#balance); // SyntaxError!Static methods and properties belong to the class itself, not to instances. They are called on the class, not on objects created from it.
class MathUtils {
static PI = 3.14159;
static square(n) {
return n * n;
}
static cube(n) {
return n * n * n;
}
}
console.log(MathUtils.PI); // 3.14159
console.log(MathUtils.square(4)); // 16
console.log(MathUtils.cube(3)); // 27
// Cannot call on instances:
const m = new MathUtils();
// m.square(4); // TypeError: m.square is not a functionStatic methods are commonly used for:
class User {
constructor(name, role) {
this.name = name;
this.role = role;
}
static createAdmin(name) {
return new User(name, 'admin');
}
static createGuest() {
return new User('Guest', 'guest');
}
}
const admin = User.createAdmin('Alice');
const guest = User.createGuest();You have already used built-in static methods: Array.isArray(), Object.keys(), Date.now(), JSON.parse(). These all belong to the class, not to instances.
The extends keyword creates a child class that inherits all properties and methods from a parent class:
class Animal {
constructor(name, sound) {
this.name = name;
this.sound = sound;
}
speak() {
return `${this.name} says ${this.sound}!`;
}
}
class Dog extends Animal {
constructor(name) {
super(name, 'Woof'); // call parent constructor
this.tricks = [];
}
learn(trick) {
this.tricks.push(trick);
}
showTricks() {
return `${this.name} knows: ${this.tricks.join(', ')}`;
}
}
const rex = new Dog('Rex');
console.log(rex.speak()); // 'Rex says Woof!' (inherited)
rex.learn('sit');
rex.learn('shake');
console.log(rex.showTricks()); // 'Rex knows: sit, shake'Key rules for inheritance:
super() must be called in the child constructor before using this. It calls the parent's constructor.super.method() to call the parent's version of an overridden method:
class Cat extends Animal {
speak() {
return super.speak() + ' (purrs)';
}
}instanceof operator checks the inheritance chain:
console.log(rex instanceof Dog); // true
console.log(rex instanceof Animal); // trueInheritance creates an "is-a" relationship. A Dog is an Animal. Use it when there is a clear hierarchy, but prefer composition (objects containing other objects) when the relationship is "has-a".
<div id="output"></div>
<script>
class Animal {
constructor(name, sound) {
this.name = name;
this.sound = sound;
}
speak() {
return `${this.name} says ${this.sound}!`;
}
}
class Dog extends Animal {
constructor(name) {
super(name, 'Woof');
this.tricks = [];
}
learn(trick) {
this.tricks.push(trick);
}
describe() {
const base = this.speak();
const tricks = this.tricks.length
? ` Knows: ${this.tricks.join(', ')}`
: ' No tricks yet.';
return base + tricks;
}
}
const dog = new Dog('Buddy');
dog.learn('sit');
dog.learn('roll over');
const cat = new Animal('Whiskers', 'Meow');
const output = document.getElementById('output');
output.innerHTML = `
<p>${dog.describe()}</p>
<p>${cat.speak()}</p>
<p>Is Dog an Animal? ${dog instanceof Animal}</p>
`;
</script>What must you call in a child class constructor before using `this`?