JavaScript Classes (With Examples)
Tags: javascript, webdev, programming, productivity
Published: 2022-09-15 18:15:26

JavaScript classes were introduced with ECMAScript 2015, they’re often described as _syntactical sugar_ over JavaScript’s existing structure of prototypical inheritance. So while classes _do not_ introduce a new inheritance model to JavaScript — they do provide syntactical simplicity. This simplicity can help us produce less error-prone & more readable code.

## Classes are just like Functions
Classes are very similar to functions. Much like functions that have both function expressions and function declarations, classes have two components: **class expressions** and **class declarations**.

## Class Declarations
Let's take a look at how we can define a class using a **class declaration**. We use the `class` keyword followed the name of the class:
```js
class Image {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}
```

## Hoisting
One important difference between **function declarations** and **class declarations** is that function declarations are hoisted and class declarations are not. You first need to declare your class and then access it, otherwise, code like the following will throw a `ReferenceError`:
```js
const p = new Image(); // ReferenceError

class Image{}
```

## Class Expressions
A **class expression** is the other way to define a class. Class expressions can be named or unnamed. Note that the name given to a named class expression is local to the class’s body. (So it’s retrievable through the classes `name` property):
```js
// An unnamed class expression
let Image = class {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
};
console.log(Image.name);
// output: "Image"

// A named class expression
let MyImage = class Image {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
};
console.log(MyImage.name);
// output: "Image"
```
_Note_: Class **expressions** are subject to the same hoisting restrictions as described previously in the Class declarations section.

## Constructors
The `constructor` method is a special method within JavaScript that we use to create and initialize an object created with a `class`. We can only use one method with the name "constructor" within a class.

Our constructor can use the `super` keyword to call the constructor of the super class (more on this later in the article!).

## Instance Properties
Instance properties must be defined inside of our class methods:
```js
class Image {
  constructor(height, width) {    
    this.height = height;
    this.width = width;
  }
}
```
If we wish to use static class-side properties and prototype data properties, these must be defined outside of the classes body declaration:
```js
Image.staticWidth = 50;
Image.prototype.prototypeWidth = 55;
```

## Field Declarations
Whilst the syntax is still considered experimental (_it’s not yet adopted by many browsers_), public and private field declarations are also worth knowing about — as often you’ll be developing with a Babel which will transpire the syntax for you.

### Public Field Declarations
Let’s revisit our example with the JavaScript field declaration syntax:
```js
class Image {
  height = 0;
  width;
  constructor(height, width) {    
    this.height = height;
    this.width = width;
  }
}
```
The difference is our fields have been declared up-front. So our class definitions become more self-documenting, and the fields are always present.

_Note_: the fields can be declared with or without a default value!

### Private Field Declarations
When we use private fields, the definition can be refined like so:
```js
class Image {
  #height = 0;
  #width;
  constructor(height, width) {    
    this.#height = height;
    this.#width = width;
  }
}
```
Private fields (declared with a `#`) cannot be referenced outside of the class, only within the class body. This ensures that your classes’ users can’t depend on internals, which may change with version changes.

Note: Private fields cannot be created later through assignment. They can only be declared up-front in a field declaration.

## Child Classes Using 'extends'
We can use the `extends` keyword with either class declarations or class expressions to create a class as a child of another class.
```js
class Vehicle{ 
  constructor(name) {
    this.name = name;
  }
  
  sound() {
    console.log(`${this.name} makes a sound.`);
  }
}
class Car extends Vehicle{
  constructor(name) {
    super(name); // call the super class constructor and pass in the name parameter
  }
  sound() {
    console.log(`The ${this.name} tooted its horn!`);
  }
}
let c = new Car('Volkswagen');
c.sound(); // The Volkswagen tooted its horn!
```
If there is a constructor present in the subclass, it needs to first call super() before using “this”.

Function-based “classes” may also be extended:
```js
function Vehicle (name) {
  this.name = name;  
}
Vehicle.prototype.sound = function () {
  console.log(`${this.name} makes a sound.`);
}
class Car extends Vehicle{
  speak() {
    console.log(`The ${this.name} tooted its horn!`);
  }
}
let c = new Car('Volkswagen');
c.sound(); // The Volkswagen tooted its horn!
```
Note: classes cannot extend regular objects! If you want to inherit from an object, use `Object.setPrototypeOf()`:
```js
const Vehicle = {
  sound() {
    console.log(`${this.name} makes a sound.`);
  }
};

class Car{
  constructor(name) {
    this.name = name;
  }
}

let c = new Car('Volkswagen');
c.sound(); // Volkswagen makes a sound.
```

## Species
If you want to return `Array` objects from an array class `MyArray`. You can do so with the “species” pattern, which lets you override the default constructors.

If using methods such as `map()` it’ll return the default constructor. Then you’ll want these methods to return a parent `Array` object, instead of the `MyArray` object. `Symbol.specieslets` you do this, like so:
```js
class MyArray extends Array {
  // Overwrite species to the parent Array constructor
  static get [Symbol.species]() { return Array; }
}

let a = new MyArray(1,2,3);
let mapped = a.map(x => x * x);

console.log(mapped instanceof MyArray); // false
console.log(mapped instanceof Array);   // true
```

## The ‘Super’ Keyword
The `super` keyword is used to call corresponding methods of super class. This is one advantage over prototype-based inheritance. Let’s see an example:
```js
class Volkswagen { 
  constructor(name) {
    this.name = name;
  }
  
  sound() {
    console.log(`${this.name} makes a sound.`);
  }
}

class Beetle extends Volkswagen {
  sound() {
    super.sound();
    console.log(`${this.name} toots it's horn.`);
  }
}

let b = new Beetle('Herbie');
b.sound(); 

// Herbie makes a sound.
// Herbie toots it's horn.
```

## Mix-ins
Mix-ins are templates for classes. An ECMAScript class can only have a single superclass, so multiple inheritance from tooling classes, for example, isn’t possible. The functionality must be provided by the superclass.

A function with a superclass as input and a subclass extending that superclass as output can be used to implement mix-ins in ECMAScript:
```js
let calculatorMixin = Base => class extends Base {
  calc() { }
};

let randomizerMixin = Base => class extends Base {
  randomize() { }
};
```
A class that uses these mix-ins can then be written like this:
```js
class First { }
class Second extends calculatorMixin(randomizerMixin(First)) { ...
```
We’ve taken a deep dive into JavaScript classes including class declarations, class expressions, constructors, instance properties, field declarations, extends, species, super and mix-ins.

## Conclusion
If you liked this blog post, follow me on [Twitter](https://twitter.com/RembertDesigns) where I post daily about Tech related things!
![Buy Me A Coffee](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ijinng8k7bf23v0o6gkj.png) If you enjoyed this article & would like to leave a tip — click [here](https://www.buymeacoffee.com/rembertdesigns)

### 🌎 Let's Connect

- [Portfolio](https://www.rembertdesigns.co/)

- [Twitter](https://twitter.com/RembertDesigns)

- [LinkedIn](https://www.linkedin.com/in/rrembert/)

- [Hashnode](https://rembertdesigns.hashnode.dev/)

- [Devto](https://dev.to/rembertdesigns)

- [Medium](https://medium.com/@rembertdesigns)

- [Github](https://github.com/rembertdesigns)

- [Codepen](https://codepen.io/rembertdesigns)