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:
- Creates a new empty object
- Binds that object to
this
- 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:
- The JS runtime first checks the instance object itself.
- If not found, it moves up and checks the
Article.prototype
object. - If still not found, it moves up and checks
Publication.prototype
. - 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!