in

JavaScript Classes: A Comprehensive Guide with Code Examples

default image

Classes are the foundation of object-oriented programming in JavaScript. As a web developer, having a solid grasp of classes allows you to write more organized, reusable code.

In this comprehensive guide, we’ll guide you through classes in JavaScript from the ground up. You’ll learn:

  • What classes are and why they matter
  • Class syntax – how to declare classes
  • Constructors, methods, fields – what goes inside classes
  • Inheritance, supercalls, static members
  • Best practices for using classes effectively

Sound good? Let’s dive in!

What Are Classes in JavaScript?

A class is a blueprint for creating objects. It encapsulates data and behaviors that belong together into one unit.

For example, we can have an Article class that defines the blueprint for creating article objects. The class would define properties like title, text, etc. It would also define methods like summarize(), print(), etc.

The class is the template while objects created from it are instances of that class.

Why Classes Matter

Classes provide important benefits that improve quality and maintainability of code:

1. Organization

Classes group related properties and methods together, keeping code compartmentalized and coherent.

For example, without classes the code for an Article would look messy:

// Article data
const articleTitle = ‘Classes in JavaScript‘ 
const articleText = ‘Classes allow cleaner code...‘

// Article behavior
function printArticle() {
  console.log(articleTitle);
  console.log(articleText);
}

function summarizeArticle() {
  //...
}

With classes, related data and behaviors are neatly together:

class Article {
  title;
  text;

  print() {
    //...
  }

  summarize() {
    //...
  }
}

This keeps code structured and readable as the app grows.

2. Reusability

Classes allow us to create any number of objects of one type easily. We can reuse the class blueprint as many times as we want.

For example, we can instantiate two article objects from the same Article class:

const article1 = new Article(); 
article1.title = ‘Reusability with Classes‘;

const article2 = new Article();
article2.title = ‘Inheritance and Polymorphism‘; 

3. Encapsulation

Classes bundle data and operations on that data within one unit. Details of the implementation are hidden within the class code.

For example, the Article class can encapsulate the logic for formatting article text. Code using Article doesn‘t worry about the formatting internals.

4. Abstraction

Classes provide abstraction by exposing essential features while hiding internal details. Other code only interacts with the class interface.

For example, the Article class abstracts functionality like summarize() and print(). Code using these methods doesn‘t need to know their implementation.

When Should You Use Classes?

Use classes when:

  • You need to model complex real-world entities like users, orders, products etc.

  • You want to compartmentalize related data and behaviors.

  • You need instances of an object created from a common blueprint.

  • You want to promote reusability of code using inheritance and polymorphism.

Let‘s now get into the syntax for declaring classes in JavaScript.

JavaScript Class Syntax

The basic syntax for declaring a class is:

class ClassName {

  // Class body...

}

By convention, class names are capitalized. Here‘s a simple example:

class Article {

}

const myArticle = new Article();

To create an instance of the class, use the new keyword. This:

  1. Creates a new empty object
  2. Binds that object to this
  3. Returns this at the end

Let‘s look at a real example.

Creating a Class in JavaScript

Let‘s build out an Article class step-by-step illustrating all its key parts.

Constructor

A class can have a special constructor() method that initializes state:

class Article {

  constructor(title, text) {
    this.title = title;
    this.text = text;
  }

}

The constructor runs automatically when we instantiate a class:

const article1 = new Article(‘Class guide‘, ‘This explians classes...‘); 

console.log(article1.title); // ‘Class guide‘

Any arguments passed to new Article() go to the constructor.

Class Fields

Fields are variables declared inside a class:

class Article {

  title;
  text;
  views = 0;

  constructor(title, text) {
    this.title = title;
    this.text = text;
  }

}

Here title, text and views are fields.

We can set field values inside the constructor like title and text. Or assign default values like views.

Fields encapsulate the object‘s data.

Methods

Methods are class functions that represent behaviors:

class Article {

  //..constructor and fields

  print() {
    console.log(`${this.title} - ${this.text}`);
  }

  summarize() {
    return `${this.title}: ${this.text.slice(0, 25)}...`;  
  }

}

We call methods like:

const article = new Article(/*..*/);

article.print(); 
console.log(article.summarize());

Methods can access the class fields and other methods using this.

Getters & Setters

Getters and setters control access to internal fields:

class Article {

  constructor(author) {
    this._author = author;
  }

  get author() {
    return this._author;
  }

  set author(newAuthor) {
    this._author = newAuthor;
  }

}

Now we track the author via a getter and setter:

const article = new Article(‘Kyle‘); 

console.log(article.author); // Kyle

article.author = ‘John‘; // Setter used

This lets us control field access and add validation logic.

Static Members

Static properties and methods belong to the class rather than instances:

class Article {
  static publisher = ‘SitePoint‘;

  constructor() {...}

  static printPublisher() {
    console.log(‘Published by‘, Article.publisher);
  }
}

Article.printPublisher(); // Static method

Static members can be accessed without creating an instance.

Common use cases for static methods are factory methods, utility functions etc.

Inheritance in JavaScript Classes

Inheritance allows a class to inherit properties and behavior from a parent class. This promotes code reuse.

The derived class is called subclass or child class. The class it inherits from is the superclass or parent class.

class Publication {
  constructor(title, author, text) {
    this.title = title;
    this.author = author; 
    this.text = text;
  }

  print() {
    console.log(this.text);
  }
}

class Article extends Publication {
  constructor(title, author, text, publisher) {
    super(title, author, text);
    this.publisher = publisher;
  }
}

const myArticle = new Article(/* ... */);
myArticle.print(); // Inherited method

This Article class inherits from Publication.

The extends keyword is used for inheritance. Inside child constructors, you must call super() to init the parent‘s state.

Article instances can now access Print methods and properties.

Overriding Members in Sub-Classes

Child classes can override members of the parent:

class Publication {
  //..
  print() {
    console.log(‘Publishing...‘); 
  }
}

class Article extends Publication {
  print() {
    super.print(); 
    console.log(‘Article customized printing‘);
  }
}

const art = new Article();
art.print(); // Overridden print()

Here Article overrides the print() method, customizing the behavior while reusing super.print() via super.

Inheritance Best Practices

When modeling inheritance relationships, ask:

  • Is the subclass truly a more specific version of the parent class?
  • Does the subclass need the data and behaviors of the parent?
  • Is the subclass reusing parent features via polymorphism?

If not, prefer composition over inheritance. Composition means having an instance of one class instantiated inside another as a field.

For example, an Article may have an Author field representing the author object. But a direct inheritance relationship between Article and Author is likely not meaningful.

Prototype Chain

JavaScript classes work via the prototype chain under the hood:

             +-------------+
             |Publication  |
             +-------------+
                ^
                |
           +-------------+     
           |   Article   |
           +-------------+

When a property/method is accessed on an instance:

  1. The JS runtime first checks the instance object itself.
  2. If not found, it moves up and checks the Article.prototype object.
  3. If still not found, it moves up and checks Publication.prototype.
  4. This continues until the property/method is eventually found.

This prototype chain lookup allows inheritance to work in JavaScript classes.

Static Members in JavaScript Classes

Static class members belong to the class itself rather than individual instances.

You can access static properties and call static methods without needing to instantiate the class first.

Static Properties

Static properties store data that‘s common to all class instances:

class Article {
  static publisher = ‘SitePoint‘;
}

// Access directly on class
console.log(Article.publisher); 

Some common uses for static properties:

  • Constants useful to all class instances like default values.

  • Shared caches like a rate limit counter.

  • Configuration settings.

Static Methods

Static methods are functions that do not depend on instance state.

For example, utility functions or factory methods:

class Article {
  static createBasic() {
    return new Article(‘Title‘, ‘Text‘); // factory method 
  }
}

// Can call without instantiating Article
const myArticle = Article.createBasic();

Common use cases for static methods:

  • Factory methods that construct appropriate class instances

  • Utility functions that don‘t require any class state, like formatters.

  • Functions for logging, validation, data manipulation etc.

Calling Static Members on Instances

Static members can also be called on class instances though that is not best practice:

const myArt = new Article();
myArt.publisher; // Works but avoid

It‘s better to call static members directly on the class itself:

Article.publisher; // Better 

JavaScript Class Design Tips

Here are some tips for effectively designing classes:

Split Up Large Classes

Don‘t create "God" objects with tons of properties and functionalities. Classes should have a focused purpose.

If a class grows too large, split it into smaller subclasses with related responsibilities.

Encapsulate Data

Class fields should not be directly manipulated from outside the class. Use getters/setters to control access.

Hide implementation details from external code. If methods or properties don‘t need to be public, make them private.

Favor Composition Over Inheritance

Prefer creating reusable components via composition rather than large inheritance hierarchies.

Composition is more flexible. For example, an Article class could have an Author field instead of inheriting directly from Author.

Avoid Deep Inheritance Chains

Deep inheritance trees make code harder to understand. Aim for breadth over depth.

For example, Animal > Mammal > Cat is likely less useful than Cat, Dog, sharing common methods via a Mammal helper.

Static Methods for Utilities

Use static methods for utilities unrelated to class instances.

For example Array.map, Number.parseInt are utilities unrelated to a specific array/number instance.

Conclusion

We‘ve explored a ton about classes in JavaScript! Here are the key takeways:

  • Classes are templates for creating objects
  • They provide organization, encapsulation, inheritance
  • Class syntax includes constructor, methods, extends etc
  • Subclasses inherit from base classes
  • Static members live directly on the class
  • Use best practices like splitting up large classes and favoring composition over inheritance

I hope you feel empowered to start using classes in your projects! They are a huge boost to code reuse and maintainability once you grasp the basics.

Let me know if you have any other questions!

Written by