Many of us have been using JavaScript for many, many years. Although you may know most of the language inside and out, there have been many advancements since June of 2015 with the release of ECMAScript Version 6 (ES6). One of the most exciting features added to the JavaScript language is the ability to use the class keyword to create classes. If you're familiar with C# or other object-oriented languages, classes are a great way to keep your code encapsulated, organized, maintainable, testable, and reusable. You now have those same capabilities in JavaScript.

In this article, you're introduced to the ES6 JavaScript class keyword. You learn to create classes with constructors into which you may or may not pass in arguments. You learn to create public, read-only, and write-only properties. Within a class, you can create private fields and methods that are only available within the class itself. Inheritance is a key feature of object-oriented programming and is accomplished in JavaScript using the extends keyword. You learn to override a method in an extended class and how to call methods in the parent class. You even learn how to extend the built-in classes in JavaScript. Finally, you build utility classes using static properties and methods.

Create Your First JavaScript Class

Classes are created within either .html or .js files. Obviously, placing classes into a .js file makes these classes reusable. To get started, create a folder named MovieSamples somewhere on your hard drive. Within this folder, add a folder named classes. Within the classes folder, add a new file named Movie.js into which you add the JavaScript shown in the following code snippet. Feel free to use VS Code, Visual Studio, or any other editor in which you are comfortable to follow along with this article.

class Movie {
    // Public Properties
    filmId;
    name;
    genres;
    actors;
}

The code starts with the class keyword followed by the name of the class. Just like in most object-oriented languages, you should use an upper-case letter for the class name. Within the curly braces, you define public properties by using the name of the property terminated with a semi-colon. The Movie class defined in this code snippet has four public properties: filmId, name, genres, and actors. These properties hold the data used to describe a movie.

Use the Movie Class

In the MovieSamples folder, create a file named class.html and add to this new file the code shown in Listing 1.

Listing 1: Create an HTML page to try out the functionality of JavaScript classes.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>ES6 JavaScript Classes</title>
  </head>

  <body>
    <header>
      <h1>ES6 JavaScript Classes</h1>
    </header>

    <main>
      <p>
        Open your F12 Browser Tools to view the results.
      </p>
    </main>

    <script src="classes/Movie.js"></script>

    <script>
      'use strict';
    </script>
  </body>
</html>

To create a new instance of the Movie class so you can place data into those properties, write the following line of code just below the use strict line within the <script> element.

let movie = new Movie();

The new keyword creates an instance of the Movie class and allocates memory into which you can assign data to the properties. The movie variable holds the reference to the Movie object in memory. You assign data through the movie variable to each property using dot notation, as shown in the following code snippet.

movie.filmId = 1;
movie.name = "Young Frankenstein";
movie.genres = "Comedy";
movie.actors = "Gene Wilder,Marty Feldman";

Assign values to each property by using the variable name, movie, followed immediately by a period (.), followed by the name of one of the public properties. On the right-hand side of the equal sign is where you specify the value to assign to each property. The movie object in memory is now holding onto this data until you wish to use it. For example, you can write the following lines of code to display each property on the console window.

    console.log(`Film ID: ${movie.filmId}`);
    console.log(`Name: ${movie.name}`);
    console.log(`Genres: ${movie.genres}`);
    console.log(`Actors: ${movie.actors}`);

Try It Out

Display the class.html file in your browser, open your F12 Developer Tools and click on the Console tab (Figure 1) to view the results of the code that has run.

Figure 1: Display each property value in the console window.
Figure 1: Display each property value in the console window.

Alternative Ways to Declare a Class

The method I just showed to declare a new class is the most common and the preferred method. However, JavaScript does allow a couple other methods of declaring a class. An alternative way to define a class is to use the class keyword with no name and assign the class name using the let statement. This declaration looks like the following code snippet:

// Can leave off the name of the class
let Movie = class {
    // Public Properties
    filmId;
    name;
    genres;
    actors;
};

This syntax looks like how you create an object literal, but because of the class keyword you cannot use the Movie variable without first creating an instance. You must still use the new keyword just like you did in the first example.

let movie = new Movie();

Define a Class with a Different Name

Another way to define a class is to use the class keyword with a name after it, but you then assign a different name using the let statement, as shown in the following code:

let Movie = class MovieClass {
    filmId;
    name;
    genres;
    actors;
}

This form of declaration is assigning an alias to the MovieClass so it's always known as Movie. The MovieClass name is not useable anywhere in your program. For example, the following code does NOT work:

// NOTE: This does NOT work!

let movie = new MovieClass();

I can't really think of a use case for this syntax, but it's valid code in JavaScript.

Classes Are Not Hoisted

In JavaScript, code is executed top to bottom. This typically means that you must create a variable or function declaration prior to calling that function or using that variable. However, you're allowed to call functions or use variables, even when the functions/variables are located further down in your <script> tag, as illustrated in the following code:

<script>
  displayMovie("Young Frankenstein");
  function displayMovie(name) {
      console.log(name);
  }
</script>

The above code works just fine because JavaScript performs what is called “hoisting.” This means JavaScript automatically moves function and variable declarations to the top of their scope. Functions and variables are moved by the interpreter to be above where they are called from. JavaScript, however, does NOT hoist class definitions. This means that you must always declare classes prior to using them. Not really a big deal, but it's something you must be aware of.

Constructors in Classes

A constructor is a method that runs when the class is instantiated using the new keyword. A constructor is created using the constructor() function name followed by any code between curly braces. The most common use of a constructor is to initialize any internal properties or fields to a default value. In the following code, you can see each of the public properties being assigned to either a zero or an empty string:

class Movie {
    // Public Properties
    filmId;
    name;
    genres;
    actors;

    constructor() {
        this.filmId = 0;
        this.name = "";
        this.genres = "";
        this.actors = "";
    }
}

Automatic Public Properties

In the Movie class, four public properties were explicitly created within the declaration of the class. However, you do not need to explicitly create public properties if you declare them in the constructor. Use the this keyword before the property name you wish to declare, as shown in the following code:

class Movie {
    // Create a constructor for this class
    constructor() {
        // Automatically create public properties
        this.filmId = 0;
        this.name = "";
        this.genres = "";
        this.actors = "";
    }
}

This code is the equivalent of the Movie class you just added the constructor to, just without the four explicitly declared public properties. You may choose to either explicitly declare your public properties, or define them within the constructor, the choice is yours.

Try It Out

If you wish, replace the previous Movie class definition with the one presented here. Save the changes to your file and display the class.html file in your browser to see the same results as shown in Figure 1.

Figure 1: Display each property value in the console window.
Figure 1: Display each property value in the console window.

Pass Arguments to a Class Constructor

A constructor is nothing more than a special kind of function. Like a function, a constructor can accept one or more parameters. Open the Movie.js file and modify the constructor of the Movie class to accept four parameters and assign the values passed into the appropriate properties.

class Movie {
    constructor(id, name, genres, actors) {
        this.filmId = id;
        this.name = name;
        this.genres = genres;
        this.actors = actors;
    }
}

Open the class.html file and modify the code that creates the instance of the Movie class and pass in arguments to the constructor to place into the appropriate properties, as shown in the following code snippet. Delete the code that assigns values to each public property within this file.

let movie = new Movie(1, "Young Frankenstein", "Comedy", 
                      "Gene Wilder,Marty Feldman");

Try It Out

Display the class.html file in your browser and, once again, you should see the same results as shown in Figure 1.

Explicitly Define Class Properties

You can explicitly define the public properties in your class and still have a constructor to assign values to those properties, as shown in the following code snippet. How you choose to create your public properties either implicitly or explicitly is a matter of style. I recommend that whichever style you choose, stay consistent throughout your applications.

class Movie {
    // Public Property Definitions
    filmId;
    name;
    genres;
    actors;

    constructor(id, name, genres, actors) {
        this.filmId = id;
        this.name = name;
        this.genres = genres;
        this.actors = actors;
    }
}

Add Public Methods to a Class

Methods in a JavaScript class are simply functions without the function keyword in front of them. To access properties of the class from within methods, you must use the this keyword before each property name just as you did in the constructor. Open the Movie.js file and add the following two methods just after the constructor of the Movie class.

// Public Method Definitions
getMovieInfo() {
    return `Film ID: ${this.filmId}\n
          Name: ${this.name}\n
          Genres: ${this.genres}\n
          Actors: ${this.actors}`;
}

asJSON() {
    return JSON.stringify(this);
}

In the getMovieInfo() method, I had to split the return string onto different lines for purposes of the printed page. Be sure to remove all the extra spaces from the literal string in the getMovieInfo() method before you run this code.

Open the class.html file and remove the four lines of code that display each public property in the console window. Instead, make a call to the getMovieInfo() method on the movie object variable, as shown in the following code snippet. Add a call to the asJSON() method as well.

let movie = new Movie(1, "Young Frankenstein", "Comedy", 
                      "Gene Wilder,Marty Feldman");

// Display the movie data
console.log(movie.getMovieInfo());

// Display the movie data as JSON
console.log(movie.asJSON());

Try It Out

Save the changes to both the Movie.js and class.html files. Display the class.html file in your browser, open the F12 Developer Tools, and your page should look like Figure 2.

Figure 2: Use methods to display the properties of a class in different formats.
Figure 2: Use methods to display the properties of a class in different formats.

Optional Chaining Operator

JavaScript returns a value of undefined if you try to access a property that does not exist in a class or an object literal. However, if you try to access a property on a nested object, JavaScript throws an exception. Use the optional chaining operator (?) to avoid the exception being thrown. The optional chaining operator returns a value of undefined on the property that is undefined.

For example, if you have an instance of the movie object and you try to access a property named releaseDate, as shown in the following code snippet, an undefined is returned.

console.log(movie.releaseDate);

Now, try to access a property under another property that does not exist, as in the following code:

console.log(movie.metaData.releaseDate);

In this case, you get an Uncaught TypeError thrown by JavaScript. The way to fix this is to add a question mark (?), the optional chaining operator, before the second to last property in the chain, as shown below:

console.log(movie.metaData?.releaseDate);

When this code is run, an undefined is returned because of the optional chaining operator. Execution stops once the name of the property or method name used just before this operator does not exist and returns an undefined instead of throwing an exception.

Try It Out

Add these lines of code to the class.html and display the page in your browser so you can see the undefined value and the exception. Comment out these first two lines and display the class.html file in the browser again. There should be no exception but instead, there should be an undefined in the console. After seeing the results, delete these three lines of code so you can continue with the rest of this article.

Class Versus a Function Constructor

JavaScript is a prototype-based language, and prior to the ES6 standard to achieve encapsulation, you created a function constructor into which you wrapped up functionality. The class keyword is just an abstraction over a prototype-based function constructor. For example, if you take the version of the Movie class you've written so far, you can translate this into the old way of declaring a class using a function constructor, as shown in Listing 2.

Listing 2: Create a class using a function constructor.

/* BEGIN: Movie Function
   Constructor Definition */
function Movie(id, name, genres, actors) {
    this.id = id;
    this.name = name;
    this.genres = genres;
    this.actors = actors;
}

Movie.prototype.getMovieInfo = function () {
    return `Film ID: ${this.filmId}\nName: ${this.name}\nGenres: 
    ${this.genres}\nActors: ${this.actors}`;
};

Movie.prototype.asJSON = function () {
    return JSON.stringify(this);
};
/* END: Movie Function Constructor Definition */

In this code, a function constructor named Movie() is created that accepts four parameters just like you passed into the constructor of the Movie class. Use the this keyword to declare properties into which you place the values from the appropriate parameters. Add on methods using the prototype object exposed from the Movie() function object to add new methods to this class. The code between this prototype function constructor and the class keyword are similar, but I think the class declaration is simpler and much easier to keep self-contained.

Try It Out

Copy the class.html file to a new file named function-constructor.html and replace the class Movie declaration with those lines shown in Listing 2. Leave the rest of the lines of code in place. Save your changes and open the function-constructor.html file in your browser. Open the F12 Developer Tools and view the console window to see the same results you saw when you ran the class.html file.

Class Inheritance

One of the best things about object-oriented programming is the ability to inherit the properties and methods of another class and add on additional properties and methods. This technique significantly cuts down the amount of code you must write. In JavaScript, use the extends keyword to inherit the properties and methods from another class. Look at the following code snippet and notice the extends and super keywords.

// Define the class structure
class RentalMovie extends Movie {
    // Constructor Definition
    constructor(id, name, genres, actors, price) {
        // Call base class constructor
        super(id, name, genres, actors);

        // Create additional public properties
        this.price = price;
    }
}

Try It Out

Add the class and the code to use the RentalMovie class to the class.html page and save the changes. Display the class.html page in your browser and you should see the movie information appear as it did before. In the console window, below the movie information, the rental price is also displayed.

Override a Class Method

If you add on a new property, you might like that property's value to appear when you call the getMovieInfo() method. You can do this by overriding the getMovieInfo() method within the RentalMovie class. Just after the constructor of the RentalMovie class, add the following code:

// Public Method Definitions
getMovieInfo() {
    // Call method in base class
    let info = super.getMovieInfo();

    // Add onto the return value
    return `${info}\nPrice: ${this.price}`;
}

Because this method has the same name as the method in the Movie class, when you create an instance of the RentalMovie class, this method is called first. Use the super keyword to invoke the getMovieInfo() method defined in the Movie class. The return value from that method is placed into a variable named info. Add a line feed using ‘\n’ followed by the price property value to the info variable and return this new string from the getMovieInfo() method. Remove the line of code console.log(Rental Price: ${movie.price}); so your code looks just like the following:

// Create an instance of a RentalMovie class
let movie = new RentalMovie(1, "Young Frankenstein", "Comedy",
    "Gene Wilder,Marty Feldman", 3.99);

// Display the movie data
console.log(movie.getMovieInfo());

Try It Out

Save the changes to the class.html file, display the page in your browser, and you should see the results from getMovieInfo() method appear in the console window including the price information.

Extend the JavaScript Number Class

Not only can you extend your classes, but you may also extend the classes pre-defined in JavaScript such as the Number class. Create a new file in your project folder named number.html and add the code shown in Listing 3.

Listing 3: Create an HTML page to illustrate extending the JavaScript Number class.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Extend the Number Class</title>
  </head>
  <body>
    <header>
      <h1>Extend the Number Class</h1>
    </header>

    <main>
      <p>
        Open your F12 Browser Tools to view the results.
      </p>
    </main>

    <script>
      'use strict';
    </script>
  </body>
</html>

Within the <script> tag, add the following code snippet to extend the Number class.

class Currency extends Number {
    asUSCurrency() {
        return this.valueOf().toLocaleString("en-US", {
            "style": "currency",
            "currency": "USD"
        });
    }
}

The code above gets all the functionality of the Number class and adds on a new method named asUSCurrency(). Use the this.valueOf() method to retrieve the current value in the Number object and pass that value to the toLocaleString() method. This method converts the numeric value into a U.S. currency-formatted string and returns that string value. Just below the definition of the Currency class, add a line of code to create a new instance of the Currency class and pass into the constructor the value 3.99, as shown in the following code:

let price = new Currency(3.99);

Write a line of code to display the value 3.99 as a U.S. currency-formatted string by calling the new asUSCurrency() method, as shown in the following line of code.

console.log(price.asUSCurrency());

Try It Out

Save the changes you made to the number.html file and display this file in your browser. Open the F12 Developer Tools and, in the console window, you should see the number displayed as $3.99.

Create Private Class Fields

A class does not necessarily need to expose all properties and/or methods outside of its scope. You might need some private variables to hold internal data, and maybe a method to perform some calculations. But this data and functionality does not need to be called from any code that instantiates the class. This is where a private field comes into play. Create a private field value by prefixing the name of the field with a pound/hash sign (#). Open the Movie.js file and, just after the constructor, add a private field as shown below:

#releaseDate;

Within the constructor, initialize this private field as shown in the following code snippet:

constructor(id, name, genres, actors) {
    // Initialize public instance properties
    this.filmId = id;
    this.name = name;
    this.genres = genres;
    this.actors = actors;

    // Initialize private fields
    this.#releaseDate = "2024-01-01";
}

In the getMovieInfo() method, return the private field value in the template literal string. This code is split onto multiple lines for the purposes of formatting in this printed magazine. Be sure to place all this code on a single line when entering it into your program.

getMovieInfo() {
    return `Film ID: ${this.filmId}
Name: ${this.name}
Genres: ${this.genres}
Actors: ${this.actors}
Release Date: ${this.#releaseDate}`;
}

Open the class.html file and add the following code at the end of the other code to see that undefined is displayed in the console window:

console.log(movie.releaseDate);

Try It Out

Display the class.html file in your browser, open the F12 Developer Tools, and the console window should look like Figure 3. Notice the releaseDate variable is displayed from the getMovieInfo() method. However, when you attempt to access it from the movie object, the result is undefined.

Figure 3: If you try to access a private field in a class, an undefined is returned instead.
Figure 3: If you try to access a private field in a class, an undefined is returned instead.

Use Getters and Setters to Create Public Properties

In addition to holding internal data for a class, private class fields are frequently used to expose data from a getter or a setter function. The keywords get and set used before a name in a class create a public property from a private class field. Open the Movie.js file and add the code shown in the following snippet:

set releaseDate(value) {
    this.#releaseDate = value;
}

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

When a set function is created, you must have a value passed into the function. This allows you to place a value on the right-hand side of an equal sign to pass the value into the set function, as shown in the following code:

movie.releaseDate = "1974-01-01";

When this code executes, the value of “1974-01-01” is passed to the value parameter in the set releaseDate(value) function. This in turn is placed into the #releaseDate private field. When you wish to query the releaseDate property through the movie object, the get function is called. For example, the following line of code calls the releaseDate get function and returns the current value of the #releaseDate private field and passes it to the console.log() function.

console.log(movie.releaseDate);

As you have now guessed, all this code is equivalent to just creating a public property on the class. So, why would you use the getter and setter functions? There are a few reasons. First, you may only wish to create a write-only property, in which case you only create a setter function to set the value for a private field. Second, you may wish to create a read-only property, in which case, you only create a getter function to return the value for the private field. Third, you may wish to add some additional code within the get or set functions to manipulate the private field prior to getting or setting the value.

Create Private Class Methods

In the scripts folder, create a new file named Product.js and add a new class definition, as shown in Listing 4. The Product class has three public properties: productId, name, and profit. It also contains two private fields named #cost and #price. There are corresponding property getter and setter functions to get and set the #cost and #price fields.

Listing 4: Create a Product class that automatically calculates the profit of a product using a private method.

class Product {
    // Public Property Definitions
    productId;
    name;
    profit = 0;

    // Private Field Definitions
    #cost;
    #price;

    // Constructor Definition
    constructor(id, name, cost, price) {
        // Initialize public instance properties
        this.productId = id;
        this.name = name;
        this.#cost = cost;
        this.#price = price;
    }

    // Public Property Getters and Setters
    get cost() {
        return this.#cost;
    }

    set cost(value) {
        this.#cost = value;
    }

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

    set price(value) {
        this.#price = value;
    }

    // Public Method Definitions
    getProductInfo() {
        return `Product ID: ${this.productId}\nName: ${this.name}\nCost: 
        ${this.cost}\nPrice: ${this.price}\nProfit: ${this.profit}`;
    }
}

Add a private method to the Product class to calculate and set the profit property whenever the #cost or #price fields are modified. A private method is created just like you create private fields: Add a pound/hash (#) sign before the name of the function. Add a new private method to the Product class named #calculateProfit(), as shown in the following code.

#calculateProfit() {
    console.log("In the calculateProfit() method");

    // Ensure you have a valid expression by
    // using the nullish coalescing operator
    this.profit = (this.price ?? 0) – (this.cost ?? 0);
}

Once you have added the #calculateProfit() method, modify each property setter to call this method after setting each of the respective private fields, as shown in the code below:

set cost(value) {
    this.#cost = value;
    // Re-calculate the profit
    this.#calculateProfit();
}

set price(value) {
    this.#price = value;
    // Re-calculate the profit
    this.#calculateProfit();
}

Within the constructor of the Product class, call the setter for both the cost and price fields so the #calculateProfit() method is called when the values for the cost and price fields are passed in. Make sure you don't set the private fields #cost and #price in the constructor because then the profit is not calculated.

Make the change to the constructor as shown in the code snippet below. Because you don't know what someone might pass into the constructor for the cost and price, it's a good idea to use the nullish coalescing operator to ensure that any null or undefined value passed in is converted to a zero.

constructor(id, name, cost, price) {
    this.productId = id;
    this.name = name;
    // Call the setters to ensure the profit is calculated
    this.cost = cost ?? 0;
    this.price = price ?? 0;
}

Create a new file in the root folder of your project named product.html and add the code shown in Listing 5.

Listing 5: Create a product page to display the Product class information.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Private Method</title>
  </head>
  <body>
    <header>
      <h1>Private Method</h1>
    </header>
    <main>
      <p>Open your F12 Browser Tools
        to view the results.</p>
    </main>
    <script src="classes/Product.js"></script>

    <script>
        'use strict';

        let prod = new Product(1, "Acme Bazooka", 100, 300);

        // Display the product data
        console.log(prod.getProductInfo());
        console.log("");
        console.log("Setting cost to 200 to force recalculation of profit.");

        prod.cost = "200";

        // Display the product data
        console.log(prod.getProductInfo());
    </script>
  </body>
</html>

Try It Out

Save all the changes you have made and display the product.html file in your browser. Open the F12 Developer Tools, and you should see a page that looks like Figure 4.

Figure 4: Create a private method to calculate the profit of a product.
Figure 4: Create a private method to calculate the profit of a product.

Static Properties and Methods

In JavaScript, use the static keyword to create static properties and methods. Static properties are used to hold data in a class that is then available through the class name. Only a single instance of this data is created the first time the class is loaded. Static methods are used for utility functions that do not rely on data in an instance of a class. Examples of static methods are those used to clone or create an instance of an object of the class. When you use static properties or methods, you access them only through the class name itself, and not through an instance variable of the class.

There are several examples of static methods in the JavaScript language itself, for example, Math.max(), Date.Now(), JSON.parse(), and JSON.stringify(). All these static methods are called on the class name itself and may or may not have arguments passed to them. Each of these methods performs an operation and returns a value.

There are several examples of static properties in the JavaScript language too. For example, Number.MIN_VALUE, Number.MAX_VALUE, and Math.PI. These are all read-only properties, but they are static because they are only accessed from the Number class, not from a number object.

Uses of Static Properties and Methods

Just as the JavaScript language uses static properties and methods, you will find multiple uses for them in your applications as well. Some property examples might be to store cached data that can be shared across all instances of a class. Define constants that are used to represent a fixed value for classes, such as an index number, into an array of pre-defined values. Another example might be a set of properties that represent configuration values needed in your application.

Static functions are utility or helper functions where you pass in an argument(s) and it returns some value. A good example of a static method would be one to make an API call to retrieve some data, or a database call to modify some data. Static methods are often used to implement a factory pattern, where a class is responsible for creating objects of other classes. Another good use of static methods is to create a clone of an existing object of the class in which the static method is declared.

Static Class Properties

To illustrate class properties, add two static properties to the Movie class. The first property is counter and is used to keep track of how many instances of the Movie class have been created. The second property is initDate, which is the date and time the Movie class is loaded by JavaScript. Open the Movie.js file and add the code shown in the following snippet:

static counter = 0;
static initDate = new Date();

Add a constructor to increment the counter each time a new instance of a Movie object is created. Notice that you are using the class name and not the keyword this because counter is a static property.

constructor() {
    // Increment # of Movie objects created
    ++Movie.counter;
}

Within the <script> element on the page, add the following code to create an instance of a Movie class. Try to access the counter property through the instance variable and you will see a value of undefined returned. Static properties cannot be accessed through an instance variable, so write code to use the Movie class name followed by the static properties to display the current static property values.

console.log("Creating a new Movie instance");

// Create an instance of a Movie class
let movie = new Movie();

// Can't access static properties through an instance
console.log(`movie.counter = ${movie.counter}`);

// Access static properties using class name only
console.log(`Movie.counter = ${Movie.counter}`);
console.log(`Movie.initDate = ${Movie.initDate}`);

console.log("");

Try It Out

Display the class.html file in your browser. Open your F12 Developer Tools in your browser and you should see the values undefined, zero, and the current date and time.

Create Another Instance of a Movie Class

Add additional code after the code you just ran to create another instance of a Movie class. After creating this new instance, display the counter static property again and you should see that the static counter property has incremented by one.

let movie2 = new Movie();

console.log("Creating another Movie instance");
console.log(`Movie.counter = ${Movie.counter}`);
console.log("");

Try It Out

Display the class.html file in your browser. Open your F12 Developer Tools in your browser and, after the console values displayed in the previous sample, you should now see that the counter static property is equal to the number two (2).

Manually Increment the Counter

Static properties can be manipulated just like instance properties. Write the code shown below to increment the counter property.

// Static public properties are read/write

console.log("Increment the Movie counter");
Movie.counter++;

Write code to display the counter and initDate properties after waiting for approximately two seconds. The reason for delaying the running of this code is to illustrate that the date/time has not changed since the Movie class was first loaded by JavaScript. Static properties contain a single value across all instances and do not change unless specifically changed by your code.

// This code runs after 2 seconds to show 
// the 'initDate' static property is the same

window.setTimeout(() => {
    // Display the static movie data
    console.log(`Movie.counter = ${Movie.counter}`);
    console.log(`Movie.initDate = ${Movie.initDate}`);
}, 2000);

Try It Out

Display the class.html file in your browser. Open your F12 Developer Tools in your browser and after the console values displayed in the previous two samples, you should now see that the counter static property is equal to the number three (3) and the initDate property has not changed between the time the script was loaded and when this code was run, which is two seconds after loading the class.

Static Read-Only Class Properties

Open the Movie.js file and change the static properties you created to be private static fields by prefixing the names with the pound/hash (#) sign. Modify the constructor to increment the #counter field. Add two static get functions to return the two static private fields.

static #counter = 0;
static #initDate = new Date();

constructor() {
    // Increment # of Movie objects created
    ++Movie.#counter;
}

static get counter() {
    return this.#counter;
}

static get initDate() {
    return this.#initDate;
}

Replace all the code within the <script> element with the following snippet. In this snippet, you are displaying the two static read-only properties. The last line attempts to set the initDate property, but because this property only has a getter, this line causes an exception to be displayed in the console window.

// Access static properties on the class name only
console.log(`Movie.counter = ${Movie.counter}`);
console.log(`Movie.initDate = ${Movie.initDate}`);
console.log("");

// Can't write to read-only static properties
// NOTE: The following line causes an exception in the console window
Movie.initDate = new Date();

Try It Out

Display the class.html file in your browser. Open your F12 Developer Tools in your browser and you should see that the counter is zero (0) and the initDate is the current date/time, and then you should see an exception where you attempted to set the initDate read-only property. You can create write-only static properties as well, but in practice, I doubt you will find much use for them.

Static Constructors

A static constructor is a special function that runs one time when the class is loaded. Use a static constructor to initialize any static properties to a valid starting value. Open the Movie.js file and add a static constructor using the static keyword followed by an open curly brace. Place code in between this opening curly brace and a closing curly brace. In the following code, initialize the static properties counter and initDate by using the this keyword. This seems counterintuitive as the this keyword is generally used for instance properties in a class, but because the code is running in the static constructor, JavaScript knows to use this to access the static properties.

// Static Constructor
static {
    console.log("In the static constructor");

    // Initialize a static property using 'this'
    this.#counter = 0;
    this.#initDate = new Date();
}

Modify the code within the <script> element to look like the following snippet.

console.log("Script is starting");

// Access static properties on the class name only
console.log(`Movie.counter = ${Movie.counter}`);
console.log(`Movie.initDate = ${Movie.initDate}`);
console.log("");

Try It Out

Display the class.html file in your browser. Open your F12 Developer Tools in your browser and you should see the message “In the static constructor” appear first, then the message “Script is starting”, in that order. This illustrates that the static constructor is run immediately when the page is loaded and before any code in the <script> element is run.

Static Class Methods

As mentioned previously, static methods can be created within a JavaScript class. To create a static method, prepend the static keyword before the name of the function. Open the Movie.js file and add the following static method to create a clone of an existing Movie object. This method accepts a parameter that is a movie object. It then creates a new instance of a Movie object and uses the Object.assign() method to copy all property values from the passed in movie object into the new instance. The cloned Movie object is then returned from this method.

// Static method to create a Movie instance
static clone(movie) {
    let clone = new Movie();
    // Clone the original data
    Object.assign(clone, movie);
    return clone;
}

Delete all the code within the <script> element. Add code to create an instance of a Movie object and call the getMovieInfo() method to display the movie information, as shown in the following code snippet:

// Create an instance of a Movie class
let movie = new Movie(1, "Young Frankenstein", "Comedy", 
                      "Gene Wilder,Marty Feldman");

// Display the movie data
console.log(movie.getMovieInfo());
console.log("");

Add code to create a clone of the movie object by calling the clone() method passing in the movie object variable. Assign the return value from the clone() method into a variable named clone. Change the name property on the clone object and call the getMovieInfo() method to display the movie information with the new movie name. Finally, call the getMovieInfo() method one more time on the original movie object to show that the two objects are distinct from one another.

// Clone the original object
let clone = Movie.clone(movie);
clone.name = "Changed Movie Name";

// Display the new movie data
console.log(clone.getMovieInfo());
console.log("");

// Display the original movie data
console.log(movie.getMovieInfo());

Try It Out

Display the class.html file in your browser. Open your F12 Developer Tools in your browser and you should see the different movie information in the console window.

Math Helper Class

A good use of static properties and methods is to create a class with nothing but utility helper properties and methods. To illustrate, create a folder named classes and add a new file named MathHelper.js. Add the following code into this new file:

class MathHelper {
    static get PI() {
        return 3.14159;
    }

    static add(op1, op2) {
        return op1 + op2;
    }

    static multiply(op1, op2) {
        return op1 * op2;
    }
}

The MathHelper class contains a single read-only property named PI that returns the value of PI to the fifth most significant digit. Two methods add() and multiply() are also created. Although you don't really need to create these methods because addition and multiplication are easily done in JavaScript, I wanted to show an example of a class with nothing but static properties and methods. To test the MathHelper class, create a file named math-helper.html and add the HTML shown in Listing 6 into the new file.

Listing 6: Create code to test the static properties and methods of the MathHelper class.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Math Helper Class</title>
  </head>
  <body>
    <header>
      <h1>Math Helper Class</h1>
    </header>

    <main>
      <p>Open your F12 Browser Tools to view the results.</p>
    </main>

    <script src="classes/MathHelper.js"></script>

    <script>
        'use strict';

        console.log(`PI = ${MathHelper.PI}`);
        console.log("");
        console.log(`MathHelper.add(42, 42) = ${MathHelper.add(42, 42)}`);
        console.log(`MathHelper.multiply(42, 42) = ${MathHelper.multiply(42, 42)}`);
    </script>
  </body>
</html>

Try It Out

Display the math-helper.html file in your browser. Open your F12 Developer Tools in your browser and you should see the value of PI displayed in the console window along with the results from calling the add() and multiply() methods.

Application Settings Class

Another time you might need to use static in a class is for application-wide application settings. In all but the most simple of applications, you most likely have several global values that you wish to use among all your different HTML pages. Examples of global values include, the URL used to access API endpoints, the port number to use, a connection string to connect to a SQL database, or the name of a database when using IndexedDB. Add a new file named ApplicationSettings.js within the classes folder and add the code shown in Listing 7.

Listing 7: Create an application settings class to hold global values for your application.

class ApplicationSettings {
    // Private Static Property Definitions
    static #apiUrl;
    static #port;

    // Public Static Property Definitions
    static get apiUrl() {
        return this.#apiUrl;
    }

    static get port() {
        return this.#port;
    }

    // Public Method Definitions
    static load() {
        // TODO: Load these from a configuration file
        this.#apiUrl = "https://www.pdsa.com/api";;
        this.#port = "8080";
    }
}

To test the ApplicationSettings class, create a file named application-settings.html and add the HTML shown in Listing 8. Within the <script> element, you first make a call to the static method named load() to initialize the apiUrl and port properties. These values are hard coded in this simple sample, but you may wish to read them from a configuration file in your application.

Listing 8: Add an HTML page to test the ApplicationSettings class.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Application Settings Class</title>
  </head>
  <body>
    <header>
      <h1>Application Settings Class</h1>
    </header>

    <main>
      <p>Open your F12 Browser Tools to view the results.</p>
    </main>

    <script src="classes/ApplicationSettings.js"></script>

    <script>
        'use strict';

        // Load application settings
        ApplicationSettings.load();

        console.log(`ApplicationSettings.apiUrl = ${ApplicationSettings.apiUrl}`);
        console.log(`ApplicationSettings.port = ${ApplicationSettings.port}`);
    </script>
  </body>
</html>

Try It Out

Display the application-settings.html file in your browser to see the values contained in each of the static properties.

Summary

In this article, you were introduced to using classes in JavaScript. You learned to define classes with properties and methods. You saw a few different methods of instantiating a class and learned that classes are not hoisted and must be defined before any code that uses the class. There are many options for creating and working with properties of a class. You have the option of automatic properties, declaring properties explicitly, creating read-only, and write-only properties. Inheritance is one of the best features of classes and you can take full advantage of this feature including overriding methods. I hope you start taking advantage of JavaScript classes in your applications.