Skip to content
wannacode

static: what all objects share

Problem

Every product is #1

The shop wants every new product to get the next number: 1, 2, 3… You put a counter #nextId in the class. The constructor gives the product the current number and increases the counter:

class Product {
#nextId = 1;
#id;
#name;
constructor(name) {
this.#name = name;
this.#id = this.#nextId;
this.#nextId++;
}
toString() {
return `#${this.#id} ${this.#name}`;
}
}
const bread = new Product("Bread");
const milk = new Product("Milk");
const coffee = new Product("Coffee");
console.log(String(bread));
console.log(String(milk));
console.log(String(coffee));
Output
#1 Bread
#1 Milk
#1 Coffee

They are all #1. The reason is that #nextId is a field of the object, just like the name. Every new product gets its own counter, which starts at 1. The bread's counter never reaches the milk.

The idea

A field of the class, not of the object

The counter is not part of one product. It is shared by all products. Add static:

static #nextId = 1;

A static field is a field of the class. There is one, no matter how many objects you create, and all the objects share it. In the constructor, you reach it through the class name: Product.#nextId.

shop.js
class Product {
static #nextId = 1;
#id;
#name;
constructor(name) {
this.#name = name;
this.#id = Product.#nextId;
Product.#nextId++;
}
toString() {
return `#${this.#id} ${this.#name}`;
}
}
const bread = new Product("Bread");
const milk = new Product("Milk");
const coffee = new Product("Coffee");
console.log(String(bread));
console.log(String(milk));
console.log(String(coffee));
Output
#1 Bread
#2 Milk
#3 Coffee

The bread takes 1 and makes the counter 2. The milk sees the same counter, takes 2 and makes it 3.

static #nextId = 1;
constructor(name) {
this.#name = name;
this.#id = Product.#nextId;
Product.#nextId++;
}
const bread = new Product("Bread");
const milk = new Product("Milk");
const coffee = new Product("Coffee");
Step by step, in words
  1. #nextId is static: one field in the class, not one in each object.
  2. new Product("Bread"): a new object. The constructor stores the name.
  3. this.#id = Product.#nextId: the object takes the current number, 1.
  4. Product.#nextId++ changes the shared counter: now it is 2.
  5. Milk: the same counter. It takes 2 and makes it 3.
  6. Coffee: it takes 3 and makes it 4.
  7. There is one counter for all of them, so the numbers go in order.

A static field is not visible through an object. From outside, you reach it through the class name (Order.count):

class Order {
static count = 0;
}
const order = new Order();
console.log(Order.count, order.count);
Output
0 undefined

Try it

Shared and own

What will the program print?

class Order {
static count = 0;
items = 0;
constructor() {
Order.count++;
this.items++;
}
}
const a = new Order();
const b = new Order();
const c = new Order();
console.log(Order.count, c.items);
Answer

The idea

A static method: work without an object

The same goes for methods. A static method is a method of the class. You call it through the class name, without new:

class Calculator {
static add(a, b) {
return a + b;
}
static average(values) {
let sum = 0;
for (const v of values) {
sum += v;
}
return sum / values.length;
}
}
console.log(Calculator.add(10, 20));
const grades = [5, 6, 4, 6];
console.log(Calculator.average(grades));
console.log(Math.max(3, 8));
Output
30
5.25
8

Calculator.add does not need an object: everything it needs comes as parameters. The methods of Math are like that too: Math.max, Math.sqrt. You do not make objects from Math, and new Math() stops the program:

Error
TypeError: Math is not a constructor

JavaScript has no main: the program is the lines of the file, from top to bottom. That is why the functions from the "Functions" module did not need a class.

More precisely, if you are curious

The reverse also works: in a static method, you can create an object and call its methods.

Try it

Find the bug

tea.label() stops the program. Which line has the bug?

Pick the line with the bug.

The idea

Constants

VAT is 20% for all products, and it does not change while the program runs. A value like that is a constant:

const VAT_RATE = 0.20;
class Prices {
static withVat(price) {
return price * (1 + VAT_RATE);
}
}
console.log(Prices.withVat(50));
console.log(`VAT: ${VAT_RATE * 100}%`);
Output
60
VAT: 20%

const means that the variable is set once and cannot be changed. You put it outside the class, at the top of the file. Constant names use capital letters and underscores: VAT_RATE.

If someone tries to change it,

VAT_RATE = 0.25;

the program stops:

Error
TypeError: Assignment to constant variable.

You win twice: the number 0.20 is written in one place with a clear name, and nobody can change it by mistake.

More precisely, if you are curious

A static field in the class is not a constant: it can be changed from outside.

class Prices {
static VAT_RATE = 0.20;
}
Prices.VAT_RATE = 0.25;
console.log(Prices.VAT_RATE);
Output
0.25

Sometimes the value is different for each object, but it must not change after the object is created. The product number is like that. Make it a private field with a getter and no setter (the "Encapsulation" lesson):

class Product {
static #nextId = 1;
#id;
#name;
constructor(name) {
this.#name = name;
this.#id = Product.#nextId;
Product.#nextId++;
}
get id() {
return this.#id;
}
}

coffee.id reads the number, and coffee.id = 5 does not change it: the field #id is private and has only a getter.

Try it

What is missing?

A year has 12 months, and that will not change. What goes in the blank?
 MONTHS = 12;

The idea

When static and when not

Ask yourself: "Does this belong to a specific object?"

  • Yes (the balance of the account, the name of the product, withdrawing from an account): a field or a method of the object, without static.
  • No (the number of all accounts, VAT, the sum of two numbers): static.

A common mistake is to make everything static so you do not have to make objects. Then all the "objects" share the same data, and you are back at the problem with the parallel arrays from the first lesson.

Exercise

What all objects share

Solve the tasks in your editor.

1. Orders. An Order class with a customer and an amount. Every new order gets the next number from a static counter. A static getter count tells how many orders have been created.

The program
const orders = [
new Order("Maria", 45.5),
new Order("Peter", 120),
new Order("Elena", 18.9),
];
for (const o of orders) {
console.log(String(o));
}
console.log(`Orders: ${Order.count}`);
Output
Order #1: Maria, €45.5
Order #2: Peter, €120
Order #3: Elena, €18.9
Orders: 3

2. Calculator. A Calculator class with static methods for adding, subtracting and multiplying whole numbers, and for dividing decimal numbers.

The program
console.log(Calculator.add(10, 20));
console.log(Calculator.subtract(10, 15));
console.log(Calculator.multiply(6, 7));
console.log(Calculator.divide(5, 2));
Output
30
-5
42
2.5

3. Exchange. A Converter class with constants for the rate: €1 = 1.08 US dollars and €1 = 0.85 British pounds (the rates are examples). Static methods convert an amount in euros to dollars and to pounds. For €20:

Output
20 EUR = 21.6 USD
20 EUR = 17 GBP

More tasks

  • Numbered accounts. Add to the Account from the "Encapsulation" lesson an account number that comes from a static counter and a getter without a setter for the number.
  • Exchange both ways. Add methods to Converter that convert from dollars and from pounds to euros. Convert €20 to dollars and back to euros. Do you get exactly 20?
  • A limit. The shop accepts at most 3 products in a cart. Add a constant for that, and make the add method refuse the fourth one.

In short

The module in short

  • A class is your own type, and an object is a specific instance of it, created with new. The variable points to the object. Your own type
  • constructor makes sure the object is created whole. this is the current object. Constructors and this
  • Instance methods work with the object's fields. toString() says what it looks like as text. Behavior
  • # fields are private. From outside, you reach them only through get and set, which check. Encapsulation
  • static is what the class shares: counters and helper methods. Constants are const.

Data and rules in one place that nobody can get around: that is object-oriented programming. In the next module, classes will start to share code with each other.

Test

Check yourself

1. You create 5 objects from a class with the field static count = 0. How many copies of count are in memory?
2. What can a static method NOT do?
3. Order has static count = 0. What does order.count give for an object order?
4. What happens with VAT_RATE = 0.25; if VAT_RATE is declared with const?
5. What is a good candidate for a static method?

Made by Ivo Gergov as part of Evo Tech's campaign for accessible programming education ·