Introduction to Classes and OOP with JavaScript

Dan Pastori avatar
Dan Pastori September 20th, 2022

To be honest, it still blows my mind that you can write Object Oriented JavaScript. Without even using TypeScript! Recently, we re-factored AmplitudeJS 6.0 to be Object Oriented and honestly, I’m loving it. I feel like OOP JavaScript really allows you to make some well-structured, dynamic apps and functional libraries.

With that being said, let’s run through the basics of making a class in JavaScript!

Defining Your Class

Let’s pretend we are making a class that handles play buttons in our app, similar to how we do with AmplitudeJS. To define a class, you can either make 1 class per file (preferred for code reusability) or export multiple classes per file. I believe 1 class per file is the way to go. Let’s add a class that handles the play buttons:

export class PlayButton {

}

The code above may look familiar. It’s very similar to how you’d export default modules or functions within Vue, React or your own library. However, notice the class token word. This allows us to use some JavaScript OOP within our app! To import this class into another file, you just import it like:

import { PlayButton } from './PlayButton.js'

Perfect! Next up, let’s instantiate our class.

Creating an Object from a Class in JavaScript

If you are familiar with OOP, this will look very familiar. Once you have your class defined and imported, you can instantiate it like this:

const playButton = new PlayButton()

Now we can call all the public methods, access public properties, etc. But first, let’s talk about a constructor() method.

Constructor Methods with JavaScript Classes

Like other object oriented languages, you can define a constructor() method that gets called on instantiation. This is extremely convenient to help set up some defaults for your object. In JavaScript, you can do the same thing! Let’s look at our PlayButton class. Say we want to pass in the color of our play buttons. We’d add a constructor() method like this:

export class PlayButton {
	constructor( color ){

	}
}

Right now we don’t do anything with the color variable, but we will in the next step. If you were to add this variable on instantiation, it’d look like:

const playButton = new PlayButton( 'red' );

Great! Now let’s do something with variable.

Public and Private Properties within JavaScript Classes

Now that we have a variable that is passed to our constructor() method, let’s assign it to a public or private property within our class. In JavaScript you can do either public or private scoping within your classes. If a variably is scoped to be public you can access it directly on the object itself. If it’s private you will have to expose it through a method. This is the same as most other object oriented languages.

Let’s make the color variable private by doing the following:

export class PlayButton {
	#color;

	constructor( color ){
		this.#color = color;
	}
}

The big difference to note is, that the # symbol before the property name denotes its visibility. Private variables start with a # sign in JavaScript. Just remove the # sign and it’d be public. You also have to reference the local property within the class using the this keyword. While we are on the subject of class properties, let’s touch on static class properties.

Static Properties

Static properties within a JavaScript class, like other object oriented languages, don’t change. You also don’t have to have an instantiation of the class to reference it. One way I use static variables is define a query string used to identify an HTML element the class responds to. Within AmplitudeJS I set static properties to query specific strings of elements. These query strings don’t change and can be used in other parts of the app without instantiating a class.

To set a static property, apply the static keyword before the property:

export class PlayButton {
	static playButtonQuery = '.play-button';
	
	#color;

	constructor( color ){
		this.#color = color;
	}
}

Now you can access that property without instantiating the class:

PlayButton.playButtonQuery;

Let’s add a little bit more functionality to our class with some methods.

Public and Private Methods in JavaScript Classes

Just like properties, you can scope public and private methods within JavaScript classes. Also, similar to properties, private methods are prefixed with a #. Let’s say we want to add a method to set all play button background color to the color provided. We’d have to have a public method that we can call when we instantiate our PlayButton class. Then we will have a private method that helps out. Let’s combine everything together now:

export class PlayButton {
	static playButtonQuery = '.play-button';

	#color;

	constructor( color ){
		this.#color = color;
	}

	setBackgroundColor(){
		let elements = document.querySelectorAll( PlayButton.playButtonQuery );
		this.#changeBackgroundColor( elements );
	}

	#changeBackgroundColor( elements ){
		elements.forEach( ( element ) => {
			element.style.backgroundColor = this.#color;
		})
	}
}

What we did was add two methods setBackgroundColor() which doesn’t accept any parameters, and #changeBackgroundColor( elements ) which is private and accepts a parameter named elements which is an array of HTML elements that match our query.

Let’s start with our public method setBackgroundColor(). In order to call this, we’d run the following code:

const playButton = new PlayButton( 'red' );
playButton.setBackgroundColor();

One thing to note about our public method is that it references our static query. We do this by calling the name of the class PlayButton.playButtonQuery . Since it’s static, we want to reference it outside the scope of our object. Once we get all the elements we pass them down to the #changeBackgroundColor() method which is private and we iterate over the elements setting the background color.

Let’s say you wanted some functionality that doesn’t require instantiation. You can add static methods as well.

Static Methods

Just like static properties, static methods don’t require any instantiation to run. I like to consider static methods almost like small helper methods. Let’s say you want just a count of how many play buttons are on the screen, but you don’t want to instantiate a new object every time. You could add the following method:

export class PlayButton {
	static playButtonQuery = '.play-button';

	#color;

	constructor( color ){
		this.#color = color;
	}

	setBackgroundColor(){
		let elements = document.querySelectorAll( PlayButton.playButtonQuery );
		this.#changeBackgroundColor( elements );
	}

	#changeBackgroundColor( elements ){
		elements.forEach( ( element ) => {
			element.style.backgroundColor = this.#color;
		})
	}

	static getPlayButtonCount(){
		return document.querySelectorAll( PlayButton.playButtonQuery ).length;
	}
}

And then call it like this:

let playButtonCount = PlayButton.getPlayButtonCount();

Notice how we referenced the playButtonQuery within the static method? That’s alright since it’s a static property as well!

Quick note on all static aspects of object oriented programming. You can not call any thing that’s NOT static when you call a static method. That means no public or private properties can be called from static methods. Only static properties.

Conclusion

A lot of JavaScript’s object oriented patterns should look very familiar if you’ve come from other object oriented languages. For me, I wrote this tutorial as a compilation of notes I took when I discovered you could write Object Oriented JavaScript. Just being exposed more to the syntax and the possibilities was what I needed to get started so hopefully it helped you as well. Of course, MDN has some great information on the subject of classes in JavaScript as well.

If you want emails to your inbox on the latest tutorials, sign up for our mailing list! Or if you have questions, you can reach out to me on Twitter (@danpastori).

Support future content

The Ultimate Guide to Building APIs and Single-Page Applications with Laravel + VueJS + Capacitor book cover.

Psst... any earnings that we make off of our book is being reinvested to bringing you more content. If you like what you read, consider getting our book or get sweet perks by becoming a sponsor.

Gold Sponsor SnapShooter Backups

Written By Dan

Dan Pastori avatar Dan Pastori

Builder, creator, and maker. Dan Pastori is a Laravel certified developer with over 10 years experience in full stack development. When you aren't finding Dan exploring new techniques in programming, catch him at the beach or hiking in the National Parks.

Like this? Subscribe

We’re privacy advocates. We will never spam you and we only want to send you emails that you actually want to receive. One-click unsubscribes are instantly honored.

What topics are you interested in?
Are you interested in our upcoming book?