More articles

Working with ES6 classes and objects 

Written by
Filed under
Published on

ES2015 introduced a new syntax into the JavaScript specification that finally allows us to create classes in a much cleaner way than the previous prototype based functional approach. Just for comparison, here is the older way of defining a class in JavaScript:

function Animal(name, species)
{
    this.name = name;
    this.species = species;
}

let cat = new Animal('steve', 'cat');

And here is the more syntactically accurate way to declare that exact same class using the newest specifications, though be it the exact same mechanism is used internally.

class Animal
{
    constructor(name, species) {
        this.name = name;
        this.species = species;
    }
}

let dog = new Animal('bruce', 'dog');

But from a programmer's perspective, it is definitely nice to finally be able to scan through the code and have a more immediate and direct knowing on what exactly is happening.

In the past, you essentially had to determine whether a function in JavaScript was being as a class or whether it was in fact just a function. Because at the end of the day, a class is just a collection of values and methods if we are to be honest. And the inherent meaning comes down to whatever it is that you are trying to accomplish.

Class declaration

As you saw in the example above, we can now declare a class using the class identifier followed by the class name. But you also have the option of declaring an unnamed class as follows:

let Animal = class {
    constructor(name, species) {
        this.name = name;
        this.species = species;
    }
};
console.log(Animal.name);

Using named classes will give you the benefit of being to create instances of the class on the fly throughout your code, however.

Constructor

The constructor syntax is defined using the constructor method name along with a list of parameterized values that will be used to set the various class properties. The one thing to note, is that you cannot use the keyword more than once per class declaration. If you wanted to define multiple types of constructors, then you are left with the option of using optional parameters in JavaScript.

Hoisting

In programming terms, hoisting refers to a feature that some programming languages have in which they automatically push variable declarations to the top of the page before any translation or compilation is performed. This ensures that whatever variables you have declared are indeed available to any of the code throughout the page.

The true is not the same for class declarations, however. In JavaScript, classes must be defined first before they can be instantiated. The following would produce an error for example.

let cow = new Animal('henry', 'cow');
class Animal
{
    constructor(name, species){
        this.name = name;
	this.species = species;
    }
}

Getter functions

Getter functions are common in OOP languages and are mainly used for returning processed data back from a class object. What do I mean by processed exactly? It could be any type of calculation or display output method used on a property that you don't necessarily want updating the actual values. Take the following class declaration for example:

class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }

  // Getter
  get area() {
    return this.calcArea();
  }

  // Method
  calcArea() {
    return this.height * this.width;
  }
}

In the example above, the area() getter function would call the calcArea() method to calculate the Rectangles calculated area and would return the value back to the caller. It won't modify any data on the class and is mainly just used to retrieve values in this case. Which is kind of how you want to think about getter() methods.

Methods vs static methods

There are 2 types of methods that you can define inside of a typical JavaScript class.

Typical methods

Standard methods are defined just as you would define a regular function in JavaScript, with a slight shorthand twist. We can define class functions without the function keyword:

let cow = new Animal('henry', 'cow');
class Animal
{
    constructor(name, species){
        this.name = name;
	this.species = species;
    }

    // class method
    sayName()
    {
        console.log(this.name);
    }
}

To reference any class property inside of the declared method, you will need to prefix the variable with the this keyword.

Static methods

Static methods are declared using the static keyword before the method name. They do not belong to any instant of a class and are called using the name of the class as the prefix.

class Animal
{
    constructor(name, species) {
        this.name = name;
        this.species = species;
    }

    static doSomething(){
        // cool code goes here
    }
}

let dog = new Animal('bruce', 'dog');
dog.doSomething()    // this would be an error
Animal.doSomething() // this is correct

Static methods are mainly used as utility methods and can perform functions that are not related to the class instants data, but that might still be related to the overall inherent meaning of the class.

Instance properties

Instance properties are values that belong to every instant of a class and in JavaScript they must be defined inside of class methods. An example would be the constructor method. These values belong to each object, whether they are set or not.

constructor(name, species) {
        this.name = name;
        this.species = species;
    }

So far JavaScript does not have a formal way of adding additional instance properties without the bulky process of including them into the constructor. There is a recommendation currently in the works for class field declarations, which you can read more about down below, but as of now they do not work in all browsers such as Firefox or Safari.

It is not impossible however to include additional properties in a class and not have them defined in the constructor method. You will just have to go the traditional route of defining them outside of the class declaration as prototype properties as such:

class Animal
{
    constructor(name, species) {
        this.name = name;
        this.species = species;
    }
}

Animal.age = 20;	// belongs to the class itself
Animal.prototype.prototypeAge = 25;	// belongs to every instance of this class

This will still work, however it is indeed a bulkier process and will undoubtedly be replaced by the more formal field declaration explained down below.

Field Declarations

Field declarations are not an official specification just yet and so browser compatibility is spotty at best. But they represent a more 'truer' way (if there were such a thing) of declaring properties in classes. At least a more traditional route, like with other languages such as C++ and C#. Using field declarations, the following would be equivalent to using the prototype based approach on top.

class Animal
{
    age = 0;	// field property with a default value
    constructor(name, species) {
        this.name = name;
        this.species = species;
    }
}
let cow = new Animal('bruce', 'cow');
cow.age = 20;

This will currently work in certain browsers, such as Chrome. So if you are interested in experimenting with the concept, that would be your best bet. Otherwise, stick to prototypes for now.

Private fields

Similar to the public field declarations defined above, the new specification also allows you to define private properties. These properties can only be accessed from within the class definition body itself and not outside.

class Animal
{
    #age = 0;	// private field property with a default value
    constructor(name, species) {
        this.name = name;
        this.species = species;
    }
}
let cow = new Animal('bruce', 'cow');
cow.age = 20;	// not allowed

Again, this is not a fully supported feature yet and should not be used on any production level code. The benefit of private properties is that you can still have data fields that are required for your code to function without exposing them to any client code.

And there you have it folks. A more formal way to implement classes and object principles in JavaScript. I hope you found this post helpful and if so, leave me a comment, send me a message and hit the like button down below to get more related topics in a future post.

Walter G. is a software engineer, startup co-founder, former CTO of several tech companies and currently teaches programming for a coding bootcamp. He has been blogging for the past 5 years and is an avid BMX rider, bio-hacker and performance enthusiast.
If you read this far, then I hope you enjoyed this post and found it useful! Consider adding to my daily coffee intake to continue to provide better and more helpful articles in the future!
Maybe later

Discussion / Comments / Questions

No messages posted yet

Add a comment

Send me your weekly newsletter filled with awesome ideas
Post comment