Learn to Code via Tutorials on Repl.it!

← Back to all posts
JavaScript Classes/OOP
h
MattDESTROYER (18)

Programming often involves lots of repetition, this can be quiet tedious and not very efficient. Object Oriented Programming (OOP) is just one way to reduce the amount of repetition in your code and make it generally more clean and understandable.

Note: This tutorial assumes you at least know the basics about JavaScript, and more specifically, objects.

Objects, a quick refresher:

In JavaScript any variable can be defined as an object.

let myObject = {
	
};

Objects can have properties:

let myObject = {
	stringProperty: "string",
	numberProperty: 1,
	booleanProperty: true,
	functionProperty: function (parameter) {
		alert(parameter);
	}
};

We can access these properties using dot notation (.):

myObject.functionProperty(myObject.stringProperty);
myObject.functionProperty(myObject.numberProperty);
myObject.functionProperty(myObject.booleanProperty);

Alright, now you're refreshed on objects. Let's learn about classes.

Classes (The actual tutorial)

First of all, what is a class? Well, you can think of a class as a template for creating lots of objects. Consider a car, we could have a simple car class which contains information to create a car. We could then pass in different information into our car class to produce different cars, trust me, this will make a lot more sense once you see it in action. Classes are everywhere in JavaScript, you might not even have been aware you were using them! An Array is a class, a String is a class, Math is obviously a class (it is a bit different because we mainly use its static functions which we will learn about later), heck even the console which you might have used, belongs to a class. I won't talk about numbers because they are a bit confusing (there are different types of 'numbers') in JavaScript compared to other languages.

In JavaScript a class can be declared using the class keyword, followed by the class name, and lastly an opening and closing curly brace ({}).

class MyFirstClass {
	
}

To create an instance of a class, we use the new keyword, followed by the class name and any parameters (just like a function). We can assign a variable the value of our class instance:

let myFirstClassMadeObject = new MyFirstClass();

Cool! But that doesn't do anything yet and our class is not useful on it's own.

Let's go with the example of cars I used above, let's create an empty Car class.

class Car {
	
}

Now we need to learn about constructors. A constructor is like a function that is called everytime an instance of a class is created. Inside a class we put constructor, followed by an opening bracket ((), then any parameters we want to take in, followed by a closing bracket ()), then like any other function we add an opening and closing curly brace ({}):

constructor(param1, param2) {
	
}

Let's add a constructor to our Car class, let's take in two parameters, name and brand.

class Car {
	constructor(name, brand) {
		
	}
}

Alright, we're getting somewhere, but this STILL does nothing. The next thing we need to learn is something that many people hate... It is ... (drumroll) ... the this keyword! Now many people hate JavaScript, and those people usually especially hate Object Oriented JavaScript. I think this is usually because of misunderstanding or just not understanding JavaScript and its uses e.t.c, if you understand classes and this you'll be fine and probably see why these are so useful. This (very punny) is probably going to be confusing so bear with me as I attempt to explain. this, when used in a class, is a direct reference to the specific object we are manipulating. Let's go back a step, in objects if we have a property that is a function, when we use this, it is the same as using the object. e.g:

let obj = {
	data: ["a", "b", "c"],
	onePlusOne: function () {
		alert(obj.data);
		alert(this.data);
	}
};

obj.onePlusOne();

In the above example, this.data is accessing the current object's (that being a variable named obj) data property. obj.data is accessing a variable named obj's data property. Hopefully that sort of makes sense, if not, don't worry, you will learn about this by using it (or you could look it up in your browser after you finish this tutorial). For now we will just keep going and you should be able to understand what is going on. Let's store the input Car's name and brand in properties, to do this we will use dot notation. In the same way you can create a property of an object using dot notation, we will do so with this.

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

Note: sometimes in classes a naming convention of starting variables with underscores is used but we will look at that when we learn getters and setters.

Now whenever we create an instance of a Car, it will automatically create properties to store the Car's name and brand. Let's try it out!

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

// Note: parameters for the constructor
// are input in brackets next to class
// name when the new keyword is used
let car1 = new Car("Carolla", "Toyota");
let car2 = new Car("Mustang", "Ford");

alert(car1.name);
alert(car1.brand);

alert(car2.name);
alert(car2.brand);

Alright, let's move on to creating functionality for our classes. Let's make a function to generate a numberplate for our Cars:

class Car {
	constructor(name, brand) {
		this.name = name;
		this.brand = brand;
	}
	
	createNumberplate() {
		// Yes, I really just wrote out the whole alphabet and the ten digits...
		// you don't need to understand the function
		// (it basically just generates a string of
		// six random characters)
		let chars = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"];
		let result = "";
		for (let i = 0; i < 6; i++) result += chars[Math.floor(Math.random() * chars.length)];
		this.numberplate = result;
		// you don't need this part, it's just useful
		// for testing
		return this.numberplate;
	}
}

Cool! Now we have a function that will generate numberplates for our Cars!

class Car {
	constructor(name, brand) {
		this.name = name;
		this.brand = brand;
	}
	
	createNumberplate() {
		let chars = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"];
		let result = "";
		for (let i = 0; i < 6; i++) result += chars[Math.floor(Math.random() * chars.length)];
		this.numberplate = result;
		return this.numberplate;
	}
}

let car1 = new Car("Carolla", "Toyota");
let car2 = new Car("Mustang", "Ford");

car1.createNumberplate();
car2.createNumberplate();

// Note: calling Car.numberplate will return undefined
// if you don't call Car.createNumberplate first
alert(car1.numberplate);
alert(car2.numberplate);

Alright, let's look at a different class now... Let's take a look at the Math class which part of all JavaScript programs. In the Math class there are a special type of functions called static functions. A static function is a function that will act the same for every object of a class. Instead of calling objectName.staticFunction, we call className.staticFunction.

class Addition {
	// this probably isn't the greatest example
	// of using static but it does demonstrate
	// how static can be used to organise
	// things
	static add(num1, num2) {
		return num1 + num2;
	}
}

alert(Addition.add(1, 1));
alert(Addition.add(2, 3));

There are also static properties which are quiet similar to static functions.

class Tree {
	static treeCount = 0;
	
	constructor() {
		Tree.treeCount++;
	}
}

alert(Tree.treeCount); // 0
let a = new Tree();
alert(Tree.treeCount); // 1
let b = new Tree();
alert(Tree.treeCount); // 2
let c = new Tree();
alert(Tree.treeCount); // 3

Now let's take a look at getters and setters. These basicly are ways of making it easy (or hard) to access and manipulate properties. To create a getter we add get followed by a name (used to get a property/value) followed by and opening and closing bracket(()) and an opening and closing curly brace ({}). We also need to return a value. A setter is the same but with set replacing get and we take in a parameter because want to assign value to a property.

class User {
	constructor(name, password) {
		this.username = name;
		this.password = password;
	}
	
	get user() {
		return this.username;
	}
	
	get pass() {
		return this.password;
	}
	
	set newUser(newUsername) {
		this.username = newUsername;
	}
	
	set newPass(newPassword) {
		this.password = newPassword;
	}
}

let user1 = new User("Owner", 12345);
alert(user1.user);
alert(user1.pass);
user1.newUser = "User1";
user1.newPass = 54321;
alert(user1.user);
alert(user1.pass);

(Obviously you don't want a login system to be that insecure...)

Sometimes we don't want people to directly access properties of objects made by our class, in those cases often we add an underscore (_) in front of the property names we don't want people to access directly, and then add a getter function without the underscore:

class HiddenProperty {
	constructor(param) {
		this._property = param;
	}
	
	get property() {
		return this._property;
	}
}

We can also restrict access to a property by for example having an empty setter function:

class RestrictedProperty {
	constructor(param) {
		this._property = param;
	}
	
	get property() {
		return this._property;
	}
	
	set property(value) {
		alert("Setting 'property' to '" + value + "' failed!");
	}
}

let restrictedExample = new RestrictedProperty("string");
restrictedExample.property = "not a string";
alert(restrictedExample.property);

Now let's take a couple steps backward, all that we have done is possible without classes, and for that reason some people dislike classes because they are (quoting xfinnbar, and a lot of other people) "just syntactic sugar". If you want to do some reading here are some links: Website 1, Website 2, Website 3. Basically classes are just a neater (depending on your view) way of using OOP in JavaScript then using functions. This is entirely subjective and people will probably tell you one or the other is better, for all intents and purposes they are the same, just use whichever you find easier or neater or understand better.

Let's go back to our Car class:

class Car {
	constructor(name, brand) {
		this.name = name;
		this.brand = brand;
	}
	
	createNumberplate() {
		// Yes, I really just wrote out the whole alphabet and the ten digits...
		// you don't need to understand the function
		// (it basically just generates a string of
		// six random characters)
		let chars = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"];
		let result = "";
		for (let i = 0; i < 6; i++) result += chars[Math.floor(Math.random() * chars.length)];
		this.numberplate = result;
		// you don't need this part, it's just useful
		// for testing
		return this.numberplate;
	}
}

Cool! Now we have a function that will generate numberplates for our Cars!

// we can do this:
function Car(name, brand) {
	this.name = name;
	this.brand = brand;
}
// OR this:
var Car = function(name, brand) {
	this.name = name;
	this.brand = brand;
}

// this is a protoype
Car.prototype.createNumberplate = function() {
	let chars = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"];
	let result = "";
	for (let i = 0; i < 6; i++) result += chars[Math.floor(Math.random() * chars.length)];
	this.numberplate = result;
	return this.numberplate;
}

That looks quiet different, to what we had before, but it's the same! Essentially, our class is a function, when called it creates an instance of an object. Then, for every instance of our object, we have created a prototype, which in this case, creates a numberplate for our Car. We can create objects with these functions in exactly the same way as we did when we used new to create instances of our class. These two ways of creating a Car are one and the same, they are identical in the program. We can add prototypes to our function at any time.

We can create static functions for our Car function by simply removing the prototype statement which we had previously:

Car.staticFunction = function() {
	
}

That's all for now, I will probably update this though!
Hope you enjoyed this tutorial. :)

Comments
hotnewtop
xfinnbar (147)

I recommend not to use classes in JavaScript, they are just syntactic sugar for prototypal inheritance, which is more concise.

MattDESTROYER (18)

@xfinnbar Even if they are just syntactic sugar, that is literally a reason for using them, although I will add some prototypal inheritance related stuff to the tutorial.