Introduction to Classes and OOP with JavaScript
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).