Classes and Private Fields

DesiEats is growing. Ananya starts with one object for each restaurant, but soon every restaurant needs the same features: a name, an open status, and a way to accept orders.

Copying those properties and functions into every object works for a while. Then a bug appears. One part of the app changes a restaurant's order count directly, even though it should only change through a safe order method.

Rahul suggests a better design: make a class for the common restaurant shape, then keep important data private. Ananya gets a reusable blueprint and a locked kitchen cupboard for internal state.

What You Will Learn

  • What a class is and how it creates objects
  • How constructors and methods work
  • How # private fields protect internal data
  • The difference between instance methods and static methods
  • When classes help, and when a plain object is simpler

The Big Idea

A class is a blueprint for creating objects. An object is one concrete thing made from that blueprint.

Think of a class as a restaurant franchise design. The design says every branch has a name and can accept orders. Each actual branch has its own name and order count.

A private field is data that can only be read or changed inside the class. In JavaScript, private fields begin with #. They are like a locked cupboard in the restaurant kitchen: staff members use the approved counter, but customers cannot reach inside and change the ingredients.

What Is a Class?

A class groups related data and behaviour. Data is stored in fields. Behaviour is written as methods, which are functions belonging to the class.

Use new to create an object from a class.

What just happened?

  • class Restaurant defines the blueprint.
  • constructor runs automatically when new Restaurant(...) is used.
  • this.name belongs to the new object.
  • describe is a method shared by restaurant objects.
  • desiEatsKitchen is one object created from the class.

Constructors Set Up Each Object

The constructor is a special method that prepares a new object. Its parameters let every object start with different values.

A constructor is like filling out a new restaurant's registration form. The form has the same fields each time, but the answers can differ.

What just happened?

The constructor received Rahul's name and the order total. It also supplied a default status. Every new DeliveryOrder gets its own values, while all orders use the same summary method.

Methods Describe Allowed Actions

A method is an action an object knows how to perform. Instead of changing data from anywhere, callers can use a method that checks the rules first.

This is like asking the restaurant cashier to cancel an order instead of walking into the kitchen and changing the order book yourself.

What just happened?

cancel owns the cancellation rule. It checks the current status before changing it. This keeps the rule in one place and makes code review easier.

Private Fields Protect Internal State

A normal field such as this.status is public. Any code holding the object can change it:

order.status = "delivered";

Sometimes that is fine. But sensitive values such as an order count, balance, or security token should not be changed freely.

A private field starts with #. Code outside the class cannot read or write it. The class must provide public methods for safe access.

What just happened?

  • #orders exists inside each RestaurantCounter object.
  • placeOrder changes it through an approved action.
  • getOrderCount returns its value without exposing the field itself.
  • "#orders" in counter is false because private names are not public property names.

Trying to use counter.#orders outside the class is a syntax error, so the playground uses a safe public method instead.

Private fields are enforced by the JavaScript language. They are not merely a naming convention like _orders.

Private Methods and Getters

Private protection also works for methods. A private method is useful when a calculation is an internal implementation detail.

A getter looks like a property when you read it, but JavaScript runs a method behind the scenes. It is useful when a value should be calculated from private data.

What just happened?

#formatBalance can only be called by Wallet. The balance getter gives readers a safe, read-only view. There is no public setter, so outside code cannot assign a new balance through wallet.balance.

Static Methods Belong to the Class

An instance method needs one particular object. A static method belongs to the class itself and is called on the class name.

Use static methods for helpers that do not need one restaurant's private or instance data. Think of a head-office rulebook: it belongs to the whole franchise, not to one branch.

What just happened?

isValidTotal is called as DeliveryOrder.isValidTotal(...). It does not need an order object. The instance still stores its own total value.

Real-World Example: A Safe DesiEats Order

Now Ananya combines constructors, methods, private fields, a getter, and a static method. The public API says exactly what the rest of the app is allowed to do.

The private array prevents another part of the app from replacing the order's items. The class exposes small, meaningful actions instead: add an item, place the order, and read the total.

Tradeoffs and When to Use Classes

Classes are useful when:

  • Several objects share the same behaviour.
  • An object has rules that should protect its state.
  • The object has a clear lifecycle, such as cart → placed → delivered.
  • You want a readable public API for a group of related operations.

A plain object or a function can be simpler when:

  • You only have a small data record.
  • There is little or no behaviour.
  • The data does not need protected state.
  • You are transforming data rather than modelling a long-lived object.

Classes do not automatically make code better. They add structure. Use that structure when it makes the rules easier to understand.

Common Mistakes and Gotchas

1. Forgetting new

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

// ❌ Wrong: classes must be called with new
// const menu = Menu("Lunch");

// ✅ Fixed
const menu = new Menu("Lunch");

A class constructor cannot be called like a normal function.

2. Pretending _balance is private

class Account {
  _balance = 0;
}

// ❌ This is only a convention
const account = new Account();
account._balance = -500;

class SafeAccount {
  #balance = 0;

  deposit(amount) {
    if (amount > 0) this.#balance += amount;
  }
}

An underscore asks developers to be careful. A # private field is enforced by JavaScript.

3. Accessing a private field from outside

class Counter {
  #value = 0;
}

const counter = new Counter();
// ❌ Syntax error: counter.#value

Expose a method or getter when outside code needs controlled access.

4. Creating a new function inside every method call

class Order {
  // ✅ Methods declared in the class are shared through the prototype.
  summary() {
    return "Order summary";
  }
}

Do not create methods inside the constructor unless each object truly needs its own function. Shared methods usually use less memory.

Code Review Corner

Rahul asks Ananya to review this code:

class Points {
  #value = 0;

  constructor(initial) {
    this.#value = initial;
  }

  add(points) {
    this.#value += points;
  }

  get value() {
    return this.#value;
  }

  static addBonus() {
    this.#value += 10;
  }
}

const points = new Points(-50);
Points.addBonus();
console.log(points.value);
Reveal the review

There are three problems:

  1. initial is not validated, so a negative score is accepted.
  2. add accepts negative values and can reduce the score unexpectedly.
  3. static addBonus cannot access #value because static methods do not have an instance's private state. The method should receive an instance or be an instance method.

Interview Questions

What is the difference between a class and an object?

A class is a blueprint. An object is a value created from that blueprint.

What does # mean in a class?

It declares a private field or method. It can only be accessed by code inside the class that declares it.

What is the output?

class Box {
  #items = [];

  add(item) {
    this.#items.push(item);
  }

  get size() {
    return this.#items.length;
  }
}

const box = new Box();
box.add("chai");
box.add("dosa");
console.log(box.size);

Answer: 2. The getter returns the number of items in the private array.

Can a static method access an instance private field?

No. A static method belongs to the class, not to one instance. It can access static private fields, but it needs an instance reference to access instance private fields.

Are class methods copied into every object?

Normally, no. Class methods are stored on the class prototype and shared by instances. Instance fields, including private fields, belong to each object.

Practice Challenges

Easy: Restaurant status

Create a Restaurant class with a name field, an isOpen field, and an open() method. The method should change the status and return a message.

class Restaurant {
  // Add a constructor and an open method.
}

const restaurant = new Restaurant("Priya's Kitchen");
console.log(restaurant.open());

Hint: Start isOpen as false in the constructor.

Medium: Protected order total

Create an Order class with a private #total field. Add an addItem(price) method that accepts only positive finite numbers and a total getter.

class Order {
  // Add #total, addItem, and a total getter.
}

const order = new Order();
order.addItem(120);
order.addItem(80);
console.log(order.total); // → 200

Hint: Use Number.isFinite(price) and price > 0.

Hard: Order lifecycle

Build a FoodOrder class with private #status and #items. Support addItem, place, and cancel. An empty order cannot be placed, and a delivered order cannot be cancelled.

class FoodOrder {
  // Design the private state and lifecycle rules.
}

const order = new FoodOrder();
console.log(order.place());
// → Add an item first

Hint: Keep each state change inside a method so callers cannot bypass your rules.

Quick Recap

  • A class is a reusable blueprint for objects.
  • new creates an object from a class.
  • The constructor prepares each new object.
  • Methods describe the actions an object supports.
  • Fields beginning with # are truly private.
  • Getters can expose safe, calculated views of private data.
  • Static methods belong to the class, not to an instance.
  • Use classes when shared behaviour and protected state make the design clearer.

What's Next

A class can describe one kind of object, but applications often have related kinds of objects. Next, you will learn constructors and static methods in more depth, then compare inheritance with composition.