JavaScript Classes
You can (and should) create a sample web page to experiment with the code samples in this article.
By now you should be comfortable with this data structure (it's an array of objects):
let dogs = [
{name: "Bruno", breed: "Poodle", vaccinated: true, age:7},
{name: "Snoopy", breed: "Beagle", vaccinated: false, age:2},
{name: "Lassie", breed: "Collie", vaccinated: true, age:4},
{name: "Fido", breed: "Boxer", vaccinated: true, age:11},
{name: "Coco", breed: "Pit Bull", vaccinated: true, ages:5}
]
Did you happen to spot what appears to be a bug in the code above? One of the 'dog' objects has a property of 'ages', which appears to be a typo, and it could cause a bug in the program.
There is a way to create a template for 'dog' objects so that they will be consistent, and so that we can avoid typos such as the one in the code sample above. In JavaScript, and in most other languages, you can create a class, which acts as a template, or a blue-print for creating objects of a specific type.
In our example, we need a class for creating 'dog' objects, so we can define a Dog class (class names should start with uppercase letters). We'll start with a simple version of a 'Dog' class, and then we'll start to add more features to it.
Add this code to your sample file:
class Dog{
name;
breed;
vaccinated = false;
age = 0;
}
To define a class in JavaScript, start with the keyword class and than add a name. It should be a name that represents the type of object that it will create. And the name of the class should start with a capital letter. After the class name, add a pair of curly braces. Inside the the curly braces you add the property names for the type of object that the class represents. In our case we've added properties for name, breed, vaccinated, and age. Note that the vaccinated, and age properties are assigned default values (you may, or may not, want to assign default values to properties when your define classes)
Now that we've defined a simple template for 'Dog' objects, we can use it to create dog objects like so:
const dog1 = new Dog();
console.log(dog1);
Run this code, and take a look at the console log. You'll see that the dog1 variable has all of the properties that we defined in the class.
When you create an object from a class you must use the new keyword, followed by the name of the class (Dog) and then a pair of parenthesis. It looks like we are invoking a function named Dog(), and we actually are doing just that! Every class automatically has a function, known as a constructor function that you can invoke in order to create an object. It's called a 'constructor function' because it 'constructs' an object from the class and returns that object. In our case, we are storing the object that is returned in a variable named dog1.
You can actually control how the constructor function works, which is very useful. Update your Dog class to look like this:
class Dog{
// Properties
name;
breed;
vaccinated = false;
age = 0;
// Constructor function
constructor(name, breed, vaccinated, age){
this.name = name;
this.breed = breed;
this.vaccinated = vaccinated;
this.age = age;
}
}
And now let's 'construct' another 'Dog' object like so:
const dog2 = new Dog("Bruno", "Poodle", true, 7);
console.log(dog2);
When you define your own constructor function, you can add parameters to it. You can then use the parameters to set the properties of the object that is being 'constructed'.
Here's one thing that can be confusing...notice that the parameters of the constructor are named the same as the properties that they set. So, for example, we have two things called 'name' in our class. One is a property, and the other is a parameter (for the constructor). To avoid confusion over which one is which, you must include this. when referring to a property.
You aren't required to name the parameters of the constructor exactly the same as the properties. If you wanted to, you could define your constructor function like so:
// Constructor function
constructor(a, b, c, d){
this.name = a;
this.breed = b;
this.vaccinated = c;
this.age = d;
}
If you did it this way, you would still be required to use this. before the property names. Remember the rule about objects that we learned way back in Web 1?... When adding code to an object, and referring to a property of that object, you must include this..
Adding Methods to a Class
Back in Web 1, we learned you can add methods when you define an object. Let's assume that we want our dog objects to have an 'eat' method. Then our dogs array might look like this:
let dogs = [
{
name: "Bruno",
breed: "Poodle",
vaccinated: true,
age:7,
eat: function(food){
console.log( this.name + " is eating " + food)
}
},
{
name: "Snoopy",
breed: "Beagle",
vaccinated: false,
age:2,
eat: function(food){
console.log( this.name + " is eating " + food)
}
},
{
name: "Lassie",
breed: "Collie",
vaccinated: true,
age:4,
eat: function(food){
console.log( this.name + " is eating " + food)
}
},
{
name: "Fido",
breed: "Boxer",
vaccinated: true,
age:11,
eat: function(food){
console.log( this.name + " is eating " + food)
}
},
{
name: "Coco",
breed: "Pit Bull",
vaccinated: true,
ages:5,
eat: function(food){
console.log( this.name + " is eating " + food)
}
}
]
This approach is very problematic because we don't want to have to duplicate the eat() method for every single 'dog' object!
We can avoid this mess by using our Dog class. Update the Dog class to look like this:
class Dog{
// Properties
name;
breed;
vaccinated = false;
age = 0;
// Constructor function
constructor(name, breed, vaccinated, age){
this.name = name;
this.breed = breed;
this.vaccinated = vaccinated;
this.age = age;
}
// Methods
eat(food){
console.log( this.name + " is eating " + food);
}
}
Now every dog that we create by calling the Dog constructor will have an eat() method. And we only have to define the eat() method one time.
Let's go ahead and invoke the eat() method on the dog2 variable:
dog2.eat("a biscuit");
Hopefully you can see the advantage of creating a class for when you will be working with many objects that are of the same type (in our case the type was 'Dog').
Now that we've defined a Dog class, let's use it to populate the dogs array, like so:
dogs = [
new Dog("Bruno", "Poodle",true,7),
new Dog("Snoopy", "Beagle", false, 2),
new Dog("Lassie", "Collie", true, 4),
new Dog("Fido", "Boxer", true, 11),
new Dog("Coco", "Pit Bull", true, 5)
];
Finally, just for fun, let's now loop through the array and invoke the eat() method for each dog:
dogs.forEach(d => d.eat("a biscuit"));
Summary
Classes are a very important feature in many programming languages. In some languages you must put all of your code into a class.
You've seen in this lesson that classes are great as 'templates' for creating objects of a specific type, so that each object is consistent and works the same. But classes are also great for organizing your code into components. We'll be talking a lot about 'component based design' for the rest of this class, an in all of the upcoming Web classes.