Register to get access to free programming courses with interactive exercises

Static properties and methods JS: Introduction to Object Oriented Programming (OOP)

In one of the previous lessons, we implemented the Money constructor. Remember its interface:

const money1 = new Money(100);
money1.getValue(); // 100
money1.format(); // "$100"
// Doesn't change money1 itself
money1.exchangeTo('eur').getValue(); // 70

const money2 = new Money(200, 'eur');
const money3 = money2.add(money1); // 270

The amount of money and the currency are part of the specific object, but what about the rates? Below is an example of a possible constructor implementation:

class Money {
  constructor() {
    this.rates = {
      usd: {
        eur: 0.7,
      },
      eur: {
        usd: 1.2,
      },
    };
  }
}

With this class definition, each newly created object will get its own copy of the conversion rate information. Technically, this code works, but logically, it's wrong. Rates have nothing to do with a specific Money object, they determine the behavior of all Money. Imagine if we needed to expand the number of currencies or change rates while the program was running. This is possible if the rates are calculated dynamically, as in real life. This means that everything should change without having to stop the program. Since all these parameters are bound to each object individually, you would have to recreate all the objects or restart the program.

To solve this problem, we use constructor functions. Any function in JavaScript is an object, and any property added to a constructor function is available in all of its objects:

// No matter how Money itself is defined. This can be a normal constructor function or a class
// One way or another, any class inside JS is a function constructor + prototype filled with functions

Money.rates = {
  usd: {
    eur: 0.7,
  },
  eur: {
    usd: 1.2,
  },
};

Accessing the constructor property differs from calling the normal properties of the object itself. There are two main ways. The first is direct, via the Money.rates constructor function. This is the easiest way, but then you have to duplicate the name of the constructor function. The second is via the constructor property. It's a special property that gives direct access to the constructor from objects. This is the preferred method when we're inside an object:

class Money {
  constructor(value, currency = 'usd') {
    this.value = value;
    this.currency = currency;
  }

  exchangeTo(newCurrency) {
    if (this.currency === newCurrency) {
      return new Money(this.value, this.currency);
    }
    // this.constructor.rates is in the constructor function
    const newValue = this.value * this.constructor.rates[this.currency][newCurrency];
    return new Money(newValue, newCurrency);
  };

  // Other methods
}

With this approach, we have separated the concerns. The Money object itself is responsible only for its own data. The constructor's concern is general things. This allows you to change Money parameters for all objects at once:

Money.rates.usd.eur = 0.71;

You can go even further and update the data not directly, but through methods:

Money.setRate = function setRate(from, to, value) {
  // Here we address it directly, because we are in the context of the Money object (aka constructor function)
  this.rates[from][to] = value;
}

Money.setRate('usd', 'gbp', 0.6);

But be careful. Everything in the constructor function has actually become a global state. Any change is reflected on all objects at once. Sometimes this can be a good thing, as in our case, but in other cases, it can lead to the data desynchronization. Especially when processes are separated in time (asynchronous code).

Static

Such functionality is implemented using static properties and methods. Here's what it looks like:

class Money {
  // Defining the static property
  static rates = {
    usd: {
      eur: 0.7,
    },
    eur: {
      usd: 1.2,
    },
  };

  // Defining the static method
  static setRate(from, to, value) {
    this.rates[from][to] = value;
  }
}

// The usage is exactly the same as in the examples above
Money.rates.usd.eur; // 0.7
Money.setRate('usd', 'eur', 0.8);
Money.rates.usd.eur; // 0.8

// From within objects, you can refer to this.constructor

Static, like classes, are just sugar over functions. But they are becoming popular because they make code cleaner.


Recommended materials

  1. Static properties / MDN
  2. Static methods / MDN

Are there any more questions? Ask them in the Discussion section.

The Hexlet support team or other students will answer you.

For full access to the course you need a professional subscription.

A professional subscription will give you full access to all Hexlet courses, projects and lifetime access to the theory of lessons learned. You can cancel your subscription at any time.

Get access
130
courses
1000
exercises
2000+
hours of theory
3200
tests

Sign up

Programming courses for beginners and experienced developers. Start training for free

  • 130 courses, 2000+ hours of theory
  • 1000 practical tasks in a browser
  • 360 000 students
By sending this form, you agree to our Personal Policy and Service Conditions

Our graduates work in companies:

Bookmate
Health Samurai
Dualboot
ABBYY
Suggested learning programs
profession
Development of front-end components for web applications
10 months
from scratch
Start at any time

Use Hexlet to the fullest extent!

  • Ask questions about the lesson
  • Test your knowledge in quizzes
  • Practice in your browser
  • Track your progress

Sign up or sign in

By sending this form, you agree to our Personal Policy and Service Conditions
Toto Image

Ask questions if you want to discuss a theory or an exercise. Hexlet Support Team and experienced community members can help find answers and solve a problem.