How to use JavaScript Classes

By

Learn how to use JavaScript classes introduced in ES6, from defining a constructor and methods to creating objects with new and inheritance using extends.

~~~

In 2015 the ECMAScript 6 (ES6) standard introduced classes.

JavaScript has a quite uncommon way to implement inheritance: prototypical inheritance. Prototypal inheritance, while in my opinion great, is unlike most other popular programming language’s implementation of inheritance, which is class-based.

People coming from Java or Python or other languages had a hard time understanding the intricacies of prototypal inheritance, so the ECMAScript committee decided to sprinkle syntactic sugar on top of prototypical inheritance so that it resembles how class-based inheritance works in other popular implementations.

This is important: JavaScript under the hood is still the same, and you can access an object prototype in the usual way.

A class definition

This is how a class looks.

class Person {
  constructor(name) {
    this.name = name
  }

  hello() {
    return 'Hello, I am ' + this.name + '.'
  }
}

A class has an identifier, which we can use to create new objects using new ClassIdentifier().

When the object is initialized, the constructor method is called, with any parameters passed.

A class also has as many methods as it needs. In this case hello is a method and can be called on all objects derived from this class:

const flavio = new Person('Flavio')
flavio.hello()

Class inheritance

A class can extend another class, and objects initialized using that class inherit all the methods of both classes.

If the inherited class has a method with the same name as one of the classes higher in the hierarchy, the closest method takes precedence:

class Programmer extends Person {
  hello() {
    return super.hello() + ' I am a programmer.'
  }
}

const flavio = new Programmer('Flavio')
flavio.hello()

(the above program prints “Hello, I am Flavio. I am a programmer.”)

Inside a class, you can reference the parent class calling super().

Class fields

You can declare public fields on the class body. They land on each instance, and you do not have to assign them only inside the constructor:

class Person {
  age = 0

  constructor(name) {
    this.name = name
  }
}

const flavio = new Person('Flavio')
flavio.age //0

There is still no protected keyword in JavaScript. Privacy is handled with #, which we cover next.

Static methods

Normally methods are defined on the instance, not on the class.

Static methods are executed on the class instead:

class Person {
  static genericHello() {
    return 'Hello'
  }
}

Person.genericHello() //Hello

You can also use a static {} block for one-time setup on the class itself, when you need more than a simple static field.

Private fields and methods

Private fields and methods use a # prefix. They are real language features now, not a naming convention. Code outside the class cannot read or call them:

class Person {
  #name

  constructor(name) {
    this.#name = name
  }

  #greet() {
    return 'Hello, I am ' + this.#name + '.'
  }

  hello() {
    return this.#greet()
  }
}

const flavio = new Person('Flavio')
flavio.hello() //Hello, I am Flavio.
// flavio.#name //SyntaxError

You can also mark static members private with #:

class Counter {
  static #count = 0

  static next() {
    return ++Counter.#count
  }
}

A leading underscore like _name is still just a soft convention. It does not hide anything. There is no protected access level.

Getters and setters

You can add methods prefixed with get or set to create a getter and setter, which are two different pieces of code that are executed based on what you are doing: accessing the variable, or modifying its value.

class Person {
  constructor(name) {
    this._name = name
  }

  set name(value) {
    this._name = value
  }

  get name() {
    return this._name
  }
}

If you only have a getter, the property cannot be set, and any attempt at doing so (outside of the constructor, which sets the value when you initialize a new object with this class) will be ignored:

class Person {
  constructor(name) {
    this._name = name
  }

  get name() {
    return this._name
  }
}

If you only have a setter, you can change the value but not access it from the outside:

class Person {
  constructor(name) {
    this._name = name
  }

  set name(value) {
    this._name = value
  }
}

Getters and setters are very useful when you want to execute some code upon changing the property value, or if you want to create a “computed” property. You can alter the values you return by using a getter.

You can also run some code, like logging to the console or to a file when a value is changed.

Want me to talk about your product? You can sponsor this site.

~~~

Related posts about js: