|

JavaScript 30 🧬 Classes

A class is a blueprint for creating objects. It encapsulates data (properties) and functions (methods) that operate on the data. Classes provide a clean, modern syntax for object-oriented programming in JavaScript.


A Quick Look at the Example

class MyClass {
    // Class field (property with default value)
    myProperty = "default value";

    // Constructor — initializes the object
    constructor(param1, param2) {
        this.property1 = param1;
        this.property2 = param2;
    }

    // Method — a function that operates on the data
    myMethod() {
        console.log(this.property1, this.property2, this.myProperty);
    }
}

// Inheritance — SubClass extends MyClass
class SubClass extends MyClass {}

// Creating an instance
const obj = new MyClass("value1", "value2");
obj.myMethod(); // "value1 value2 default value"

a. Classes — Part 1

A class is a blueprint for creating objects. It encapsulates data (properties) and functions (methods) that operate on the data.

Class Declaration

You declare a class using the class keyword followed by the name of the class. The class body is enclosed in curly braces { }.

class MyClass {
    // class body
}

Key points:

  • Class names are typically PascalCase (e.g., MyClass, Person, BankAccount)
  • Classes are not hoisted — you must define them before using them
  • Classes are first-class citizens — you can pass them as arguments, return them, assign them to variables

Constructor Method

A special method named constructor() is used to initialize objects created from a class.

  • It’s called automatically when an object is instantiated using the new keyword
  • If you don’t provide your own constructor, JavaScript uses a default one
  • You can only have one constructor per class
class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
}

const person = new Person("Alice", 30);
console.log(person.name); // "Alice"
console.log(person.age);  // 30

Default constructor:

class Empty {
    // No constructor — JavaScript provides a default one
}

const obj = new Empty();
// Equivalent to: constructor() {}

Properties

Classes can have properties — variables that store data for individual objects. They can be defined:

MethodDescriptionExample
In constructorAssigned to thisthis.name = name;
Class fieldsDefined in class bodymyProperty = "default value";
class MyClass {
    // Class field (ES2022)
    myProperty = "default value";

    // Constructor — instance properties
    constructor(param1, param2) {
        this.property1 = param1;
        this.property2 = param2;
    }
}

const obj = new MyClass("value1", "value2");
console.log(obj.myProperty);  // "default value"
console.log(obj.property1);   // "value1"
console.log(obj.property2);   // "value2"

Class fields vs constructor properties:

AspectClass FieldsConstructor
Syntaxprop = value;this.prop = value;
When setBefore constructor body runsIn constructor body
ParametersCannot use constructor paramsCan use parameters
Use caseDefaults, private fieldsComputed values, params

b. Classes — Part 2

Methods

Classes can have methods — functions defined within the class. These methods manipulate object properties and perform actions.

class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }

    // Method
    greet() {
        return `Hello, my name is ${this.name}`;
    }

    // Method with parameters
    celebrateBirthday() {
        this.age++;
        return `Happy birthday! Now ${this.age}.`;
    }
}

const person = new Person("Alice", 30);
console.log(person.greet());             // "Hello, my name is Alice"
console.log(person.celebrateBirthday()); // "Happy birthday! Now 31."

Key points:

  • Methods are defined inside the class body but outside the constructor
  • Methods are shared across all instances (stored on the prototype)
  • No need for function keyword
  • No commas between methods

Inheritance

JavaScript supports inheritance — creating a new class that inherits properties and methods from an existing class (the parent or base class).

Use the extends keyword:

class Animal {
    constructor(name) {
        this.name = name;
    }

    speak() {
        return `${this.name} makes a sound.`;
    }
}

class Dog extends Animal {
    constructor(name, breed) {
        super(name); // Call parent constructor
        this.breed = breed;
    }

    bark() {
        return `${this.name} barks!`;
    }

    // Override parent method
    speak() {
        return `${this.name} barks.`;
    }
}

const dog = new Dog("Rex", "Labrador");
console.log(dog.speak()); // "Rex barks." (overridden)
console.log(dog.bark());  // "Rex barks!" (own method)
console.log(dog.name);    // "Rex" (inherited property)
console.log(dog.breed);   // "Labrador" (own property)

The super keyword:

UsageDescription
super(args)Calls the parent constructor
super.method()Calls a parent method

Important rules:

  • super() must be called before accessing this in a subclass constructor
  • If the parent has a constructor, the subclass must call super()
  • Method overriding — a subclass can redefine a parent’s method

Object Creation

After defining a class, you create objects (instances) using the new keyword. This calls the constructor method to initialize the new object with the provided arguments.

class Person {
    constructor(name) {
        this.name = name;
    }
}

const person = new Person("Alice");

What happens with new:

  1. A new empty object is created
  2. The constructor is called with the given arguments
  3. this refers to the new object
  4. The new object is returned (unless constructor returns another object)

Complete Example

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Classes</title>
    <style>
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            max-width: 800px;
            margin: 0 auto;
            padding: 20px;
            background: #f8f9fa;
            color: #333;
            line-height: 1.6;
        }
        h1 { color: #007bff; border-bottom: 3px solid #007bff; padding-bottom: 10px; }
        h2 { color: #28a745; border-left: 4px solid #28a745; padding-left: 15px; margin-top: 30px; }
        .demo-box {
            background: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
            margin: 15px 0;
        }
        pre {
            background: #1e1e1e;
            color: #d4d4d4;
            padding: 15px;
            border-radius: 8px;
            overflow-x: auto;
            font-family: 'Courier New', monospace;
            font-size: 0.9rem;
            line-height: 1.8;
        }
        .keyword { color: #569cd6; }
        .string { color: #ce9178; }
        .number { color: #b5cea8; }
        .function { color: #dcdcaa; }
        .comment { color: #6a9955; }
        .boolean { color: #569cd6; }
        .class-name { color: #4ec9b0; }
        #output {
            background: #e9ecef;
            padding: 15px;
            border-radius: 8px;
            margin-top: 15px;
            min-height: 40px;
            font-family: 'Courier New', monospace;
            font-size: 0.85rem;
            border-left: 4px solid #007bff;
            white-space: pre-wrap;
        }
        table {
            width: 100%;
            border-collapse: collapse;
            margin: 15px 0;
        }
        th, td {
            padding: 10px;
            border: 1px solid #ddd;
            text-align: left;
        }
        th { background: #007bff; color: white; }
        tr:nth-child(even) { background: #f8f9fa; }
        .btn {
            padding: 10px 20px;
            background: #007bff;
            color: white;
            border: none;
            border-radius: 6px;
            font-size: 1em;
            font-weight: bold;
            cursor: pointer;
            margin: 5px;
            transition: all 0.3s;
        }
        .btn:hover {
            background: #0056b3;
            transform: translateY(-2px);
        }
        .btn-success { background: #28a745; }
        .btn-success:hover { background: #1e7e34; }
        .btn-danger { background: #dc3545; }
        .btn-danger:hover { background: #a71d2a; }
        .class-display {
            font-family: 'Courier New', monospace;
            font-size: 1.05em;
            background: #f8f9fa;
            padding: 15px;
            border-radius: 8px;
            margin: 10px 0;
            border-left: 4px solid #007bff;
            white-space: pre-wrap;
            line-height: 1.8;
        }
        .input-group {
            margin: 10px 0;
        }
        .input-group label {
            display: inline-block;
            min-width: 150px;
            font-weight: bold;
        }
        .input-group input {
            padding: 8px 12px;
            border: 2px solid #ddd;
            border-radius: 6px;
            font-size: 1em;
            width: 200px;
        }
        .input-group input:focus {
            outline: none;
            border-color: #007bff;
        }
        .hierarchy {
            font-family: 'Courier New', monospace;
            font-size: 0.85rem;
            background: #1e1e1e;
            color: #d4d4d4;
            padding: 20px;
            border-radius: 8px;
            margin: 15px 0;
            white-space: pre;
            overflow-x: auto;
            line-height: 1.8;
        }
    </style>
</head>
<body>

    <h1>Classes</h1>

    <div class="demo-box">
        <h2>1. Basic Class</h2>
        <pre>
<span class="keyword">class</span> <span class="class-name">MyClass</span> {
    <span class="comment">// Class field (ES2022)</span>
    myProperty = <span class="string">"default value"</span>;

    <span class="comment">// Constructor</span>
    <span class="function">constructor</span>(param1, param2) {
        <span class="keyword">this</span>.property1 = param1;
        <span class="keyword">this</span>.property2 = param2;
    }

    <span class="comment">// Method</span>
    <span class="function">myMethod</span>() {
        console.log(<span class="keyword">this</span>.property1, <span class="keyword">this</span>.property2, <span class="keyword">this</span>.myProperty);
    }
}

<span class="keyword">const</span> obj = <span class="keyword">new</span> <span class="class-name">MyClass</span>(<span class="string">"value1"</span>, <span class="string">"value2"</span>);
obj.<span class="function">myMethod</span>(); <span class="comment">// "value1 value2 default value"</span>
        </pre>
    </div>

    <div class="demo-box">
        <h2>2. Inheritance with extends and super</h2>
        <pre>
<span class="keyword">class</span> <span class="class-name">Animal</span> {
    <span class="function">constructor</span>(name) {
        <span class="keyword">this</span>.name = name;
    }

    <span class="function">speak</span>() {
        <span class="keyword">return</span> <span class="string">`${this.name} makes a sound.`</span>;
    }
}

<span class="keyword">class</span> <span class="class-name">Dog</span> <span class="keyword">extends</span> <span class="class-name">Animal</span> {
    <span class="function">constructor</span>(name, breed) {
        <span class="keyword">super</span>(name); <span class="comment">// Call parent constructor</span>
        <span class="keyword">this</span>.breed = breed;
    }

    <span class="function">bark</span>() {
        <span class="keyword">return</span> <span class="string">`${this.name} barks!`</span>;
    }

    <span class="comment">// Override parent method</span>
    <span class="function">speak</span>() {
        <span class="keyword">return</span> <span class="string">`${this.name} barks.`</span>;
    }
}
        </pre>
    </div>

    <div class="demo-box">
        <h2>3. Class Hierarchy Visualization</h2>
        <div class="hierarchy">
<span style="color: #4ec9b0;">Animal</span> (parent class)
  ├── constructor(name)
  ├── speak()
  │
  └── <span style="color: #4ec9b0;">Dog</span> (child class, extends Animal)
        ├── constructor(name, breed)
        │     └── super(name) ← calls Animal's constructor
        ├── bark()          ← own method
        └── speak()         ← overridden method
        </div>
    </div>

    <div class="demo-box">
        <h2>4. Class Syntax Reference</h2>
        <table>
            <tr>
                <th>Feature</th>
                <th>Syntax</th>
                <th>Example</th>
            </tr>
            <tr>
                <td><strong>Declaration</strong></td>
                <td><code>class Name { }</code></td>
                <td><code>class Person { }</code></td>
            </tr>
            <tr>
                <td><strong>Constructor</strong></td>
                <td><code>constructor(params) { }</code></td>
                <td><code>constructor(name) { this.name = name; }</code></td>
            </tr>
            <tr>
                <td><strong>Class field</strong></td>
                <td><code>prop = value;</code></td>
                <td><code>age = 0;</code></td>
            </tr>
            <tr>
                <td><strong>Method</strong></td>
                <td><code>name() { }</code></td>
                <td><code>greet() { return "Hi"; }</code></td>
            </tr>
            <tr>
                <td><strong>Getter</strong></td>
                <td><code>get name() { }</code></td>
                <td><code>get fullName() { return ...; }</code></td>
            </tr>
            <tr>
                <td><strong>Setter</strong></td>
                <td><code>set name(v) { }</code></td>
                <td><code>set age(v) { this._age = v; }</code></td>
            </tr>
            <tr>
                <td><strong>Static method</strong></td>
                <td><code>static name() { }</code></td>
                <td><code>static create() { return new Person(); }</code></td>
            </tr>
            <tr>
                <td><strong>Private field</strong></td>
                <td><code>#name;</code></td>
                <td><code>#balance = 0;</code></td>
            </tr>
            <tr>
                <td><strong>Inheritance</strong></td>
                <td><code>class B extends A { }</code></td>
                <td><code>class Dog extends Animal { }</code></td>
            </tr>
            <tr>
                <td><strong>Parent call</strong></td>
                <td><code>super(args)</code></td>
                <td><code>super(name)</code></td>
            </tr>
        </table>
    </div>

    <div class="demo-box">
        <h2>5. Interactive: Class Playground</h2>
        <div class="input-group">
            <label for="nameInput">Name:</label>
            <input type="text" id="nameInput" value="Alice">
        </div>
        <div class="input-group">
            <label for="ageInput">Age:</label>
            <input type="number" id="ageInput" value="30">
        </div>
        <div style="margin: 10px 0;">
            <button class="btn btn-success" onclick="createPerson()">Create Person</button>
            <button class="btn" onclick="createDog()">Create Dog</button>
            <button class="btn" onclick="showHierarchy()">Show Hierarchy</button>
            <button class="btn btn-danger" onclick="clearOutput()">Clear</button>
        </div>
        <div class="class-display" id="classDisplay">Create an object to see its details</div>
    </div>

    <div class="demo-box">
        <h2>6. Live Output — All Examples</h2>
        <div id="output">Loading...</div>
    </div>

    <script>
        // ============================================
        // Classes — Live Demo
        // ============================================

        let results = [];

        // 1. Basic class
        results.push('📌 Basic Class:\n');

        class MyClass {
            myProperty = "default value";

            constructor(param1, param2) {
                this.property1 = param1;
                this.property2 = param2;
            }

            myMethod() {
                return `${this.property1} ${this.property2} ${this.myProperty}`;
            }
        }

        const obj = new MyClass("value1", "value2");
        results.push('  const obj = new MyClass("value1", "value2")');
        results.push('  obj.myMethod() → "' + obj.myMethod() + '"');
        results.push('');

        // 2. Constructor + properties
        results.push('📌 Constructor and Properties:\n');

        class Person {
            species = "Homo sapiens"; // Class field

            constructor(name, age) {
                this.name = name;
                this.age = age;
            }

            greet() {
                return `Hello, my name is ${this.name}`;
            }

            celebrateBirthday() {
                this.age++;
                return `Happy birthday! Now ${this.age}.`;
            }
        }

        const alice = new Person("Alice", 30);
        results.push('  const alice = new Person("Alice", 30)');
        results.push('  alice.name → "' + alice.name + '"');
        results.push('  alice.age → ' + alice.age);
        results.push('  alice.species → "' + alice.species + '" (class field)');
        results.push('  alice.greet() → "' + alice.greet() + '"');
        results.push('  alice.celebrateBirthday() → "' + alice.celebrateBirthday() + '"');
        results.push('');

        // 3. Inheritance
        results.push('📌 Inheritance:\n');

        class Animal {
            constructor(name) {
                this.name = name;
            }

            speak() {
                return `${this.name} makes a sound.`;
            }
        }

        class Dog extends Animal {
            constructor(name, breed) {
                super(name);
                this.breed = breed;
            }

            bark() {
                return `${this.name} barks!`;
            }

            speak() {
                return `${this.name} barks.`;
            }
        }

        const dog = new Dog("Rex", "Labrador");
        results.push('  const dog = new Dog("Rex", "Labrador")');
        results.push('  dog.name → "' + dog.name + '" (inherited)');
        results.push('  dog.breed → "' + dog.breed + '" (own)');
        results.push('  dog.speak() → "' + dog.speak() + '" (overridden)');
        results.push('  dog.bark() → "' + dog.bark() + '" (own)');
        results.push('  dog instanceof Dog → ' + (dog instanceof Dog));
        results.push('  dog instanceof Animal → ' + (dog instanceof Animal));
        results.push('');

        // 4. Multi-level inheritance
        results.push('📌 Multi-Level Inheritance:\n');

        class Vehicle {
            constructor(brand) {
                this.brand = brand;
            }
            start() { return `${this.brand} starts.`; }
        }

        class Car extends Vehicle {
            constructor(brand, model) {
                super(brand);
                this.model = model;
            }
            drive() { return `${this.brand} ${this.model} drives.`; }
        }

        class ElectricCar extends Car {
            constructor(brand, model, range) {
                super(brand, model);
                this.range = range;
            }
            charge() { return `Charging... ${this.range}km range.`; }
        }

        const tesla = new ElectricCar("Tesla", "Model 3", 500);
        results.push('  const tesla = new ElectricCar("Tesla", "Model 3", 500)');
        results.push('  tesla.start() → "' + tesla.start() + '" (from Vehicle)');
        results.push('  tesla.drive() → "' + tesla.drive() + '" (from Car)');
        results.push('  tesla.charge() → "' + tesla.charge() + '" (own)');
        results.push('  tesla instanceof Vehicle → ' + (tesla instanceof Vehicle));
        results.push('  tesla instanceof Car → ' + (tesla instanceof Car));
        results.push('  tesla instanceof ElectricCar → ' + (tesla instanceof ElectricCar));
        results.push('');

        // 5. Static methods
        results.push('📌 Static Methods:\n');

        class MathHelper {
            static add(a, b) {
                return a + b;
            }

            static multiply(a, b) {
                return a * b;
            }
        }

        results.push('  MathHelper.add(5, 3) → ' + MathHelper.add(5, 3));
        results.push('  MathHelper.multiply(4, 7) → ' + MathHelper.multiply(4, 7));
        results.push('  → Static methods are called on the CLASS, not instances');
        results.push('');

        // 6. Getters and setters
        results.push('📌 Getters and Setters:\n');

        class Temperature {
            constructor(celsius) {
                this._celsius = celsius;
            }

            get celsius() {
                return this._celsius;
            }

            set celsius(value) {
                if (value < -273.15) throw new Error("Below absolute zero!");
                this._celsius = value;
            }

            get fahrenheit() {
                return this._celsius * 9 / 5 + 32;
            }

            set fahrenheit(value) {
                this._celsius = (value - 32) * 5 / 9;
            }
        }

        const temp = new Temperature(25);
        results.push('  const temp = new Temperature(25)');
        results.push('  temp.celsius → ' + temp.celsius);
        results.push('  temp.fahrenheit → ' + temp.fahrenheit);
        temp.fahrenheit = 100;
        results.push('  After temp.fahrenheit = 100:');
        results.push('  temp.celsius → ' + temp.celsius.toFixed(2));
        results.push('');

        // 7. Private fields
        results.push('📌 Private Fields (#):\n');

        class BankAccount {
            #balance = 0;

            constructor(initialBalance) {
                this.#balance = initialBalance;
            }

            deposit(amount) {
                if (amount < 0) throw new Error("Cannot deposit negative");
                this.#balance += amount;
                return this.#balance;
            }

            getBalance() {
                return this.#balance;
            }
        }

        const account = new BankAccount(1000);
        account.deposit(500);
        results.push('  const account = new BankAccount(1000)');
        results.push('  account.deposit(500)');
        results.push('  account.getBalance() → ' + account.getBalance());
        results.push('  → #balance is private — cannot access from outside');
        results.push('');

        // 8. Classes vs Prototypes
        results.push('📌 Classes vs Prototypes:\n');
        results.push('  → Classes are syntactic sugar over prototypes');
        results.push('  → class Person { } is equivalent to function Person() { }');
        results.push('  → Methods defined in classes go on the prototype');
        results.push('');

        // 9. Practical: Task class
        results.push('📌 Practical: Task Manager:\n');

        class Task {
            static nextId = 1;

            constructor(title) {
                this.id = Task.nextId++;
                this.title = title;
                this.completed = false;
                this.createdAt = new Date();
            }

            complete() {
                this.completed = true;
                return `✅ Task "${this.title}" completed`;
            }

            toString() {
                const status = this.completed ? '✅' : '⬜';
                return `${status} [${this.id}] ${this.title}`;
            }
        }

        const task1 = new Task("Learn JavaScript");
        const task2 = new Task("Build a project");

        results.push('  ' + task1.toString());
        results.push('  ' + task2.toString());
        results.push('  ' + task1.complete());
        results.push('  ' + task1.toString());
        results.push('');

        // 10. Practical: Shapes
        results.push('📌 Practical: Shape Hierarchy:\n');

        class Shape {
            constructor(name) {
                this.name = name;
            }

            area() {
                return 0;
            }

            toString() {
                return `${this.name}: area = ${this.area().toFixed(2)}`;
            }
        }

        class Circle extends Shape {
            constructor(radius) {
                super("Circle");
                this.radius = radius;
            }
            area() {
                return Math.PI * this.radius ** 2;
            }
        }

        class Rectangle extends Shape {
            constructor(width, height) {
                super("Rectangle");
                this.width = width;
                this.height = height;
            }
            area() {
                return this.width * this.height;
            }
        }

        const circle = new Circle(5);
        const rect = new Rectangle(4, 6);

        results.push('  ' + circle.toString());
        results.push('  ' + rect.toString());
        results.push('  → Polymorphism: same method, different behavior!');

        document.getElementById('output').textContent = results.join('\n');

        // ============================================
        // Interactive: Class Playground
        // ============================================

        class InteractivePerson {
            constructor(name, age) {
                this.name = name;
                this.age = age;
            }

            greet() {
                return `Hello, I'm ${this.name}, ${this.age} years old.`;
            }

            isAdult() {
                return this.age >= 18;
            }
        }

        class InteractiveDog {
            constructor(name) {
                this.name = name;
            }

            speak() {
                return `${this.name} says Woof!`;
            }
        }

        function createPerson() {
            const name = document.getElementById('nameInput').value;
            const age = Number(document.getElementById('ageInput').value);
            const person = new InteractivePerson(name, age);
            const display = document.getElementById('classDisplay');

            display.textContent =
                `👤 InteractivePerson\n` +
                `─────────────────────\n` +
                `name: "${person.name}"\n` +
                `age: ${person.age}\n` +
                `greet(): "${person.greet()}"\n` +
                `isAdult(): ${person.isAdult()}\n` +
                `\n` +
                `instanceof InteractivePerson: ${person instanceof InteractivePerson}`;
        }

        function createDog() {
            const name = document.getElementById('nameInput').value;
            const dog = new InteractiveDog(name);
            const display = document.getElementById('classDisplay');

            display.textContent =
                `🐕 InteractiveDog\n` +
                `─────────────────────\n` +
                `name: "${dog.name}"\n` +
                `speak(): "${dog.speak()}"\n` +
                `\n` +
                `instanceof InteractiveDog: ${dog instanceof InteractiveDog}\n` +
                `instanceof InteractivePerson: ${dog instanceof InteractivePerson}`;
        }

        function showHierarchy() {
            const display = document.getElementById('classDisplay');
            display.textContent =
                `📊 Class Hierarchy\n` +
                `─────────────────────\n` +
                `\n` +
                `        Animal\n` +
                `       /      \\\n` +
                `     Dog      Cat\n` +
                `      |\n` +
                `   Puppy\n` +
                `\n` +
                `Each level inherits from its parent.\n` +
                `Methods can be overridden at each level.`;
        }

        function clearOutput() {
            document.getElementById('classDisplay').textContent = 'Create an object to see its details';
        }
    </script>

</body>
</html>

Quick Reference

Class Syntax

FeatureSyntaxExample
Declarationclass Name { }class Person { }
Constructorconstructor(params) { }constructor(name) { this.name = name; }
Class fieldprop = value;age = 0;
Methodname() { }greet() { return "Hi"; }
Getterget name() { }get fullName() { ... }
Setterset name(v) { }set age(v) { ... }
Static methodstatic name() { }static create() { }
Private field#name;#balance = 0;
Inheritanceclass B extends A { }class Dog extends Animal { }
Parent callsuper(args)super(name)

Key Concepts

ConceptDescription
ClassBlueprint for creating objects
ConstructorSpecial method for initialization
InstanceObject created from a class
InheritanceChild class inherits from parent
OverrideChild replaces parent’s method
superReference to parent class
StaticBelongs to class, not instances
Private (#)Only accessible inside the class

Constructor Rules

RuleDescription
One per classCan only have one constructor
Auto-calledCalled automatically by new
Must call super()In subclass before using this
OptionalDefault constructor used if omitted
Return valueReturns this by default

Best Practices

Do This:

// Use PascalCase for class names
class Person { }

// Call super() in subclass constructors
class Dog extends Animal {
    constructor(name) {
        super(name);
        this.breed = breed;
    }
}

// Use private fields for internal state
class BankAccount {
    #balance = 0;
}

// Use static methods for utility functions
class MathUtil {
    static add(a, b) { return a + b; }
}

// Use getters for computed properties
class Circle {
    get area() { return Math.PI * this.radius ** 2; }
}

// Keep classes focused on one responsibility
class UserService { }
class UserRepository { }

Don’t Do This:

// Don't forget super() in subclass
class Dog extends Animal {
    constructor(name) {
        this.name = name; // ❌ ReferenceError before super()
    }
}

// Don't use classes when plain objects work
class Point {
    constructor(x, y) { this.x = x; this.y = y; }
}
// Simpler: const point = { x: 1, y: 2 };

// Don't overuse inheritance
class FlyingSwimmingWalkingAnimal { } // ❌ Prefer composition

// Don't forget `new` when instantiating
const p = Person("Alice"); // ❌ TypeError: Cannot call class without new

// Don't use classes for singletons
class Config { } // ⚠️ Use a plain object for singletons

// Don't expose mutable state directly
class BadPerson {
    constructor() {
        this.address = {}; // ❌ Anyone can mutate
    }
}

Common Pitfalls

PitfallProblemSolution
Forgetting newTypeErrorAlways use new ClassName()
Missing super()ReferenceErrorCall super() first in subclass
HoistingClass not defined yetDefine before using
Method bindingthis lost in callbacksUse arrow functions or bind
Private field outside classSyntaxErrorOnly use # inside class body
Overusing inheritanceRigid hierarchiesPrefer composition

Classes vs Functions (Prototypes)

// Class (modern)
class Person {
    constructor(name) {
        this.name = name;
    }
    greet() {
        return `Hi, ${this.name}`;
    }
}

// Equivalent (prototype-based)
function PersonOld(name) {
    this.name = name;
}
PersonOld.prototype.greet = function() {
    return `Hi, ${this.name}`;
};

Both create the same underlying structure — classes are syntactic sugar over prototypes!


Real-World Example

// Custom Error hierarchy
class AppError extends Error {
    constructor(message, code) {
        super(message);
        this.name = 'AppError';
        this.code = code;
    }
}

class ValidationError extends AppError {
    constructor(message, field) {
        super(message, 'VALIDATION_ERROR');
        this.name = 'ValidationError';
        this.field = field;
    }
}

class NotFoundError extends AppError {
    constructor(resource) {
        super(`${resource} not found`, 'NOT_FOUND');
        this.name = 'NotFoundError';
    }
}

try {
    throw new ValidationError('Invalid email', 'email');
} catch (err) {
    console.log(err instanceof AppError);        // true
    console.log(err instanceof ValidationError); // true
    console.log(err.code);                       // "VALIDATION_ERROR"
    console.log(err.field);                      // "email"
}

Pro Tip: Classes provide a clean, modern syntax for object-oriented programming in JavaScript. Use them for:

  • Data models (User, Product, Order)
  • Services (UserService, ApiClient)
  • Custom errors (extending Error)
  • UI components (React class components)

Key rules:

  • Use PascalCase for class names
  • Always call super() before using this in subclasses
  • Use private fields (#) for internal state
  • Use static methods for class-level utilities
  • Use getters/setters for computed properties or validation

But remember: Don’t overuse classes! Plain objects and functions often work better for simple cases. Use classes when you need encapsulation, inheritance, or multiple instances of similar objects.


Stop using slow, ad-bloated tool sites! 🤮

🔎 Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)

⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free

🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!