Topic 56 of 59 JavaScript Objects, Arrays & Advanced Data Structures Interactive Tutorial

JavaScript Classes: Building Objects with Modern Class Syntax

23 mins read Last updated: 22 Aug 2026 14453 views
Quick Summary & Key Takeaway Target Skill Level: Beginner to Advanced ES6+

Bottom-Line Answer: JavaScript (JS) is a high-level, single-threaded, just-in-time (JIT) compiled programming language with first-class functions that conforms to the ECMAScript specification. It powers dynamic client-side web interactivity alongside HTML and CSS, and executes backend server runtimes via Node.js, Deno, and Bun.

Target Audience: Frontend developers, full-stack engineers, students & QA testers.
Prerequisites: Basic HTML semantics & fundamental CSS styling rules.
Primary Use Cases: Dynamic web UI, DOM manipulation, asynchronous REST APIs, Node.js backend services.

What is JavaScript Classes? Core Concept & Direct Summary

Core Objective & Problem Statement

How do you define ES6 classes, static methods, and constructor methods?

Direct Answer Summary
Use class Name { constructor() {} static method() {} } to create blueprint object definitions.

you saw the 1995 machinery underneath. Now it's time to master thecomplete modern class syntax— every feature, every keyword, every pattern — the way professional 2025 JavaScript actually writes classes.

Here's the complete toolkit at a glance — don't worry, we'll build each piece:

Constructor, methods, private fields, static members, getters, inheritance — by the end of this page, every line above will be second nature. Let's assemble the full blueprint. 🏗️

How Does JavaScript Classes Work? In-Depth Mechanics & Architecture

Static methods belong to the class constructor function itself rather than instance objects.

The Foundation: class, constructor, new 🧱

Quick refresher of the core (full story in Lesson 23):

script.js
Run Online
class Student {
  constructor(name, age) {
    // runs automatically on "new"
    this.name = name;
    // attach data to the new instance
    this.age = age;
  } introduce() {
    // shared method — lives on the prototype
    return `Hi, I'm ${this.name}, ${this.age} 👋`;
  }
}const s1 = new Student("Priya", 22);
// ✅ always with new
const s2 = new Student("Amit", 24);
console.log(s1.introduce());
// "Hi, I'm Priya, 22 👋"
console.log(s1.introduce === s2.introduce);
// true — one shared copy (Lesson 48!)

The non-negotiable rules:

script.js
Run Online
const s = Student("Priya", 22);
// ❌ TypeError — classes REQUIRE new
class student {
} // ⚠️ legal but breaks convention — Capitalize!

Class Fields: Properties Without the Constructor 🌱

Modern classes (2022+) let you declare fieldsdirectly in the class body— cleaner than packing everything into the constructor:

script.js
Run Online
class ShoppingCart {
  items =[];
  // 🌱 field — every instance starts with []
  created = new Date();
  // 🌱 initialized at construction time
  #discountApplied = false;
  // 🔒 private field (more below!)
  constructor(owner) {
    this.owner = owner;
    // constructor now handles only arguments
  } addItem(item) {
    this.items.push(item);
    // array method from Lesson 50!
    console.log(`➕ ${item} added (${this.items.length} items)`);
  }
}const cart = new ShoppingCart("Priya");
cart.addItem("Keyboard");
// ➕ Keyboard added (1 items)
cart.addItem("Mouse");
// ➕ Mouse added (2 items)
console.log(cart.items);
// ["Keyboard", "Mouse"] ✅

When to use fields vs constructor:

Put it in a field when…Put it in the constructor when…
Every instance starts with thesame valueThe value comes fromarguments
items = [],count = 0this.name = name
Cleaner than constructor clutterSetup logic/validation needed

Arrow Function Fields: Thethis-Safe Methods 🏹

Remember Lesson 29's lost-thisbug? Class fields with arrow functions solve it permanently:

script.js
Run Online
class ClickCounter {
  count = 0;
  // 🏹 arrow field — this is locked to the instance FOREVER:
  increment =() => {
    this.count++;
    console.log(`Clicks: ${this.count}`);
  };
}const counter = new ClickCounter();
const btn = document.getElementById("myBtn");
btn.addEventListener("click", counter.increment);
// ✅ borrowed, still works!
// (Lesson 29's callback disaster — completely prevented)

Regular methods losethiswhen borrowed; arrow fields never do. That's why UI components love this pattern.


Private Fields & Methods: The # Fortress 🔒

Lesson 23 introduced#fields; here's the complete privacy toolkit:

script.js
Run Online
class BankAccount {
  #balance = 0;
  // 🔒 private FIELD
  #pin;
  // declared private, set in constructor
  constructor(owner, pin) {
    this.owner = owner;
    // 🌍 public
    this.#pin = pin;
    // 🔒 private
  } // 🔒 private METHOD — only callable from inside the class:
  #verifyPin(attempt) {
    return attempt === this.#pin;
  } withdraw(amount, pin) {
    if (!this.#verifyPin(pin)) {
      // internal use only ✅
      console.log("❌ Wrong PIN!");
      return;
    } if (amount > this.#balance) {
      console.log("❌ Insufficient funds");
      return;
    } this.#balance -= amount;
    console.log(`💸 Withdrew ₹${amount}. Balance: ₹${this.#balance}`);
  } deposit(amount) {
    this.#balance += amount;
    console.log(`💰 Balance: ₹${this.#balance}`);
  }
}const acc = new BankAccount("Priya", "4321");
acc.deposit(1000);
// 💰 Balance: ₹1000
acc.withdraw(300, "4321");
// 💸 Withdrew ₹300. Balance: ₹700
acc.withdraw(300, "0000");
// ❌ Wrong PIN!
acc.#balance;
// ❌ SyntaxError — truly private!
acc.#verifyPin("4321");
// ❌ SyntaxError — private method too!

#= enforced by the language itself.Unlike the old_underscoreconvention (a polite request),#is a locked door — outside code physically cannot compile access to it. (Lesson 22's closure privacy achieves the same goal differently — closures hide data in scope;#hides it in syntax.)

Static Members: Belonging to the Class Itself 🏛️

Some things belong to theblueprint, not the instances — counters, factories, utilities. That'sstatic:

script.js
Run Online
class Player {
  static totalPlayers = 0;
  // 🏛️ static FIELD — one shared value
  constructor(name) {
    this.name = name;
    Player.totalPlayers++;
    // count every creation!
  } // 🏛️ static METHOD — called on the class, not instances:
  static resetCounter() {
    Player.totalPlayers = 0;
    console.log("Counter reset 🔄");
  } static createGuest() {
    // 🏛️ factory method!
    return new Player(`Guest${Player.totalPlayers + 1}`);
  }
}const p1 = new Player("Priya");
const p2 = new Player("Amit");
const p3 = Player.createGuest();
// 🏭 factory-created!
console.log(Player.totalPlayers);
// 3 ✅ — the class counts its children
console.log(p3.name);
// "Guest3"
Player.resetCounter();
// Counter reset 🔄
console.log(Player.totalPlayers);
// 0
// ⚠️ statics are INVISIBLE to instances:
p1.totalPlayers;
// undefined — instances can't see statics!
Player.createGuest();
// ✅ statics live on the CLASS

The mental model:instances are cookies 🍪 — statics are notes on the cookiecutteritself. You've used statics all along:Math.round(),Array.isArray(),Number.isNaN(),Object.keys()— all static methods on built-in classes!

Instance membersStatic members
Belong toEach objectThe class itself
Called onp1.namePlayer.totalPlayers
Use forPer-object data & behaviorCounters, factories, utilities
Examplescart.itemsMath.max(),Array.isArray()

Getters, Setters & Computed Names ✨

Lesson 31's guards work natively in class bodies (with#fields as their vaults):

script.js
Run Online
class Circle {
  #radius;
  constructor(radius) {
    this.radius = radius;
    // goes through the setter ✅
  } get radius() {
    // 📖 read
    return this.#radius;
  } set radius(value) {
    // ✏️ write — with a bouncer
    if (value <= 0) throw new Error("Radius must be positive! 🚫");
    this.#radius = value;
  } get area() {
    // 🧮 computed — always fresh
    return (Math.PI * this.#radius ** 2).toFixed(2);
  }
}const c = new Circle(7);
console.log(c.area);
// "153.94" ✅
c.radius = 10;
console.log(c.area);
// "314.16" — recomputed automatically!
c.radius = -5;
// ❌ Error: Radius must be positive! 🚫

Bonus: Computed Method Names

script.js
Run Online
const action = "jump";
class Player {
[action]() {
    // 🎭 a method named by a variable!
    console.log("Jumping! 🦘");
  }
}new Player().jump();
// "Jumping! 🦘"

(Rare in beginner code — but you'll recognize it in libraries. And yes, Lesson 53's well-known symbols use exactly this bracket syntax!)


Inheritance Refresher: extends & super 🧬

Lesson 23 covered the concept; here's the complete syntax in one place:

script.js
Run Online
class Vehicle {
  constructor(brand) {
    this.brand = brand;
  } start() {
    return `${this.brand} engine on 🔑`;
  }
}class ElectricCar extends Vehicle {
  #battery = 100;
  // child's own private field
  constructor(brand, range) {
    super(brand);
    // ☎️ FIRST — before any "this"!
    this.range = range;
  } start() {
    // 🎭 override (polymorphism!)
    return `${this.brand} boots silently ⚡ (${this.#battery}% battery)`;
  } describe() {
    return `${super.start()} + electric mode`;
    // ☎️ call the parent's version!
  }
}const tesla = new ElectricCar("Tesla", 450);
console.log(tesla.start());
// "Tesla boots silently ⚡ (100% battery)"
console.log(tesla.describe());
// "Tesla engine on 🔑 + electric mode"

The two faces ofsuper:

  • super(args)— in the constructor, calls the parent's constructor (mandatory first linebeforethis)
  • super.method()— in methods, calls the parent's version of a method

🛠️ Mini Project: The Complete Class — Task Manager ✅

Every feature from this lesson in one realistic class:

script.js
Run Online
// ✅ TaskManager — fields, private, static, getters, everything
class TaskManager {
  static totalTasks = 0;
  // 🏛️ class-level counter
  #tasks =[];
  // 🔒 private task list
  constructor(owner) {
    this.owner = owner;
  } addTask(title) {
    const task = {
      id: TaskManager.totalTasks + 1, title: title.trim(), // Lesson 52's string cleanup!
      done: false
    };
    this.#tasks.push(task);
    TaskManager.totalTasks++;
    console.log(`➕ "${task.title}" added (#${task.id})`);
  } completeTask(id) {
    const task = this.#tasks.find(t => t.id === id);
    // Lesson 50's find!
    if (!task) {
      console.log(`❌ No task with id ${id}`);
      return;
    } task.done = true;
    console.log(`✅ "${task.title}" completed!`);
  } get pending() {
    // 📖 computed property
    return this.#tasks.filter(t => !t.done);
  } get summary() {
    const done = this.#tasks.length - this.pending.length;
    return `${done}/${this.#tasks.length} done 📊`;
  } static compareProgress(managerA, managerB) {
    // 🏛️ static utility
    return managerA.pending.length <= managerB.pending.length ? `${managerA.owner} is more on top of things! 🏆` : `${managerB.owner} is more on top of things! 🏆`;
  }
}const priya = new TaskManager("Priya");
priya.addTask("  Finish lesson 55  ");
priya.addTask("Build a project");
priya.completeTask(1);
console.log(`Progress: ${priya.summary}`);
// "Progress: 1/2 done 📊"
console.log(`Pending: ${priya.pending.length} task(s)`);
// 1
console.log(TaskManager.totalTasks);
// 2
// priya.#tasks;                                  // ❌ private!

Output:

script.js
Run Online
➕ "Finish lesson 55" added(#1)➕ "Build a project" added(#2)✅ "Finish lesson 55" completed!Progress: 1/2 done 📊Pending: 1 task(s)2

Fields, privacy, statics, getters, find/filter — one class, the complete modern toolkit working together. 🏆

The Complete Class Syntax Card 🗂️

script.js
Run Online
class Example extends Parent {
  // 🌱 Fields
  instanceField = "default";
  #privateField = "hidden";
  static staticField = "shared";
  // 🏗️ Constructor
  constructor(arg) {
    super(arg);
    // if extending — always first!
    this.arg = arg;
  } // 📦 Methods
  method() {
  } #privateMethod() {
  } // 🔒
  static staticMethod() {
  } // 🏛️
  arrowField =() => {
  };
  // 🏹 this-safe field
  // 🚪 Accessors
  get prop() {
  } set prop(v) {
  }
}

Common Beginner Mistakes (And Fixes)

❌ Mistake🔍 What Happens✅ Fix
Student("Priya")withoutnewTypeError — alwaysnew Student("Priya")
Commas between methodsSyntaxError — classes aren't object literalsNo commas in class bodies
thisbeforesuper()ReferenceErrorsuper() is the constructor's first act
Accessing statics via instancesundefinedClassName.staticMember
Expecting_privateto be privateFully accessible 😬#privatefor real privacy
Borrowed methods losingthisundefined errors in callbacksArrow function fields 🏹
Fields shadowing confusionthis.x =in constructor vs field duplicatesPick one home per property

Quick Self-Check: Lesson 55 Quiz 🧠

  1. What's the difference between a class field and a constructor assignment?
  2. What doesstaticmean, and how do you call a static method?
  3. What's the output?
  1. script.js
    Run Online
    class Counter {
      static count = 0;
      constructor() {
        Counter.count++;
      }
    }new Counter();
    new Counter();
    console.log(Counter.count);
  2. Why can'tacc.#balancebe accessed from outside the class — and what error does it produce?
  3. When does an arrow-function class field beat a regular method?

(Answers: 1. Fields initialize with the same default for every instance and live in the class body; constructor assignments receive per-instance arguments — both end up on the instance · 2. Static members belong to the class itself, not instances — call them on the class:Counter.count,ClassName.method()· 3. 2 — each construction increments the one shared static counter · 4. # fields are hard-private, enforced by the language — outside access is a SyntaxError (the code won't even parse) · 5. When the function will be borrowed as a callback (event handlers, timers) — arrow fields keepthislocked to the instance, regular methods lose it)



JavaScript Decision Matrix & Syntax Comparison

Understanding the mechanical differences between variable declarations and execution environments is critical for writing performant, bug-free modern JavaScript.

Variable Declarations: var vs let vs const
Feature / Propertyvar (Legacy ES5)let (Modern ES6+)const (Modern ES6+)
ScopeFunction ScopeBlock Scope ({})Block Scope ({})
Hoisting BehaviorHoisted (initialized as undefined)Hoisted (Temporal Dead Zone - ReferenceError)Hoisted (Temporal Dead Zone - ReferenceError)
Reassignable✓ Yes✓ Yes✗ No (Immutable binding)
Redeclarable in Same Scope✓ Yes (Error-prone)✗ No (SyntaxError)✗ No (SyntaxError)
Window Object BindingCreates property on windowDoes not attach to windowDoes not attach to window
Environment Decision Matrix: Browser Client-Side vs Node.js Server-Side
DimensionClient-Side (Web Browser)Server-Side (Node.js / Deno)
Global Objectwindow, document, navigatorglobal, process, globalThis
DOM & UI Manipulation✓ Full access (document.querySelector)✗ No DOM (Headless / Non-UI)
File System & OS Access✗ Blocked (Sandboxed security)✓ Full access (fs, os, child_process)
Primary FocusInteractive UI, animations, client stateREST APIs, microservices, databases, I/O streaming

Runnable JavaScript Code Example & Practical Implementation

script.js
Run Online
class Car {
  constructor(brand) {
    this.brand = brand;
  } drive() {
    return `${this.brand} is driving`;
  } static isVehicle(obj) {
    return obj instanceof Car;
  }
}const myCar = new Car('Tesla');
console.log(myCar.drive());
// 'Tesla is driving'
console.log(Car.isVehicle(myCar));
// true

Frequently Asked Questions & Technical Interview Insights

Technical Interview & Viva Questions

Frequently asked questions for frontend & full-stack roles
Q

Q: What is the purpose of private class fields (#field) in ES2022?

A: Private class fields (#field) enforce hard language-level privacy; accessing them outside the class throws a SyntaxError.

Related Tools, Tutorials & Authoritative References

Recommended Interactive Tools

Authoritative Standards & Citations