JavaScript Classes Explained: Complete Tutorial

Classes are templates for creating objects with shared methods and data. Learn how to define a class, write a constructor, add methods, and create instances in JavaScript.

7 min read

A JavaScript class is a blueprint for creating objects that share the same shape and behavior. Instead of writing a separate object literal for every user, product, or component, you define a class once and use new to stamp out as many instances as you need.

javascriptjavascript
class User {
  constructor(name, email) {
    this.name = name;
    this.email = email;
  }
 
  greet() {
    return `Hello, I am ${this.name}`;
  }
}
 
const alice = new User("Alice", "alice@example.com");
console.log(alice.greet()); // "Hello, I am Alice"

The class block defines the template, and new User(...) creates an instance from it. Each instance gets its own name and email, but every instance shares a single copy of the greet method. That is the core idea behind classes: define once, share behavior, own unique data.

The Constructor

The constructor method runs automatically when you call new on a class. Its job is to set up the instance with its own properties, using whatever parameter names make sense for that class.

javascriptjavascript
class Book {
  constructor(title, author, pages) {
    this.title = title;
    this.author = author;
    this.pages = pages;
  }
}
 
const book = new Book("Eloquent JavaScript", "Marijn Haverbeke", 472);
console.log(book.title); // "Eloquent JavaScript"
console.log(book.pages); // 472

Each argument passed to the constructor lands inside this, which refers to the new object being built. Every property assigned to this becomes an own property of that specific instance. If you skip the constructor entirely, JavaScript supplies an empty one for you. A class can only have one constructor; adding a second throws a syntax error.

Instance Methods

Methods written inside the class body, outside the constructor, become shared methods on the prototype. Every instance uses the same function in memory instead of storing its own copy.

javascriptjavascript
class Counter {
  constructor(start = 0) {
    this.value = start;
  }
  increment() {
    this.value += 1;
    return this.value;
  }
}
 
const counter = new Counter(5);
console.log(counter.increment()); // 6
console.log(counter.increment()); // 7

The value property is separate for each counter instance, but the increment method itself is shared across every instance. Calling counter.increment() finds that shared method and runs it with the counter object as its context.

Getters and Setters

A getter looks like a plain property read but runs a function behind the scenes. A setter intercepts an assignment the same way. Use them for computed properties or validation without forcing callers to write a method call.

javascriptjavascript
class Temperature {
  constructor(celsius) {
    this._celsius = celsius;
  }
  get fahrenheit() {
    return this._celsius * 9 / 5 + 32;
  }
  set fahrenheit(value) {
    this._celsius = (value - 32) * 5 / 9;
  }
}
 
const temp = new Temperature(25);
console.log(temp.fahrenheit); // 77

Reading temp.fahrenheit calls the getter, and writing to it calls the setter. The leading underscore on the stored field is only a naming convention that signals "internal"; it is not truly private. For real privacy, use private class fields.

Class Fields

Class fields let you declare instance properties directly in the class body, without writing them inside the constructor. This is cleaner when the starting value does not depend on constructor arguments.

javascriptjavascript
class TodoItem {
  completed = false;
  constructor(text) {
    this.text = text;
  }
  toggle() {
    this.completed = !this.completed;
  }
}
 
const todo = new TodoItem("Buy groceries");
console.log(todo.completed); // false
todo.toggle();
console.log(todo.completed); // true

Fields declared this way are set before the constructor body runs, and they become own properties on every instance. A field without an initial value simply defaults to undefined.

Static Methods and Fields

A static member belongs to the class itself, not to any instance. Static methods work well as utility functions, and static fields work well as shared constants or configuration.

javascriptjavascript
class MathUtils {
  static PI = 3.14159;
 
  static square(x) {
    return x * x;
  }
}
 
console.log(MathUtils.PI);        // 3.14159
console.log(MathUtils.square(5)); // 25
 
const utils = new MathUtils();
// utils.square(5); throws TypeError: not a function

Static members live on the class function itself, so they are never reachable from an instance. That final commented-out line would throw at runtime if you actually ran it.

How Classes Work Under the Hood

Classes are not a new kind of thing in JavaScript. They are a cleaner way to write the constructor-function and prototype-method pattern that existed long before the class keyword. Understanding this connection helps when you read older code or debug inheritance.

Class-to-prototype relationship

Writing a class makes JavaScript create a constructor function and a prototype object behind the scenes. Calling new User("Alice") creates an object whose internal prototype link points to that shared object. The greet method lives there, so every instance finds it by following the link instead of storing its own copy.

The class syntax is roughly equivalent to a plain constructor function with a method attached to its prototype property afterward. Both forms produce the same result, but the class version is always strict mode and reads more clearly.

When to Use a Class

Classes shine when you need many objects that share behavior:

  • Modeling data: a User, Product, Order, or Post class with properties and methods.
  • UI components: a Button, Modal, or Accordion class that manages DOM state.
  • Services: an HttpClient or Logger class with shared configuration and methods.
  • State management: a Store or Observable class that holds state and notifies listeners.

If you only need one plain object with no shared methods, a simple object literal is lighter and often the better choice. Do not reach for a class just to group a few values together.

For a deeper look at inheritance built on classes, see class inheritance.

Rune AI

Rune AI

Key Insights

  • A class is a template for creating objects with shared structure and behavior.
  • The constructor method runs once per instance and sets up the object's own properties.
  • Methods defined in the class body are shared across all instances via the prototype.
  • Use get and set for computed property access that looks like a simple property read.
  • Static methods and fields belong to the class itself, not to any instance.
RunePowered by Rune AI

Frequently Asked Questions

Are JavaScript classes the same as classes in Java or C++?

No. JavaScript classes are syntax sugar over the language's existing prototype-based inheritance. Under the hood, a class is still a function, and methods are still added to the prototype. Understanding prototypes will help you understand classes better.

Can I use classes in older browsers?

Classes (ES6) are supported in all modern browsers since 2016. They do not work in Internet Explorer. If you need to support IE, you can transpile classes to prototype-based code with a tool like Babel.

Should I use classes or factory functions?

Both work. Classes are the standard choice when you need many instances that share methods through the prototype. Factory functions (plain functions that return objects) are simpler when you do not need inheritance or shared method memory.

Conclusion

Classes make object creation predictable and readable. Define your object shape once with a constructor and shared methods, then create as many instances as you need. Start with a simple class, add methods, and use getters for computed properties. When you need private data, reach for private fields, and when you need shared logic, use static methods.