(2019) JavaScript OOP
Objectives
Describe the difference between classes and objects
Use JavaScript syntax to define a class
Instantiate an object from a class
Explain the use of the
constructor
methodUtilize the
this
keyword in the scope of class variables and methodsUnderstand class variables and how they differ from instance variables
Objects and Classes
JavaScript is an object oriented programming language (or at least it does its best to approximate one). Object oriented programming is characterized by the creation and use of collections of data and behavior that act like everyday physical objects. Every day we interact with objects like chairs, beverages, cars, other people, etc. These objects have properties that define them and behaviors we can execute to interact with them. Over many years, programmers have found that designing systems to reflect discrete, everyday objects makes the systems easier to understand, write, test, and maintain.
You will hear people say that "everything is an object" in JavaScropt. What this means is that nearly every variable you declare already has a set of properties and functions that it can use because it is an object. Every string, number, array, object, etc has a set of behaviors and properties that are "baked-in" because they are instances of a class. For example, every string you declare is an instance of a built-in JavaScript String class and because of that you can use any of the built-in string functions like toUpperCase() and toLowerCase() on your own string.
Frequently, we want to design our own special data types that hold our own specific data and organize it in a certain way. JavaScript gives us a string and a number, but what if we want to store a bunch of data where each item contains a string and an number and a function that prints them out nicely? We can make a class for that and then each object we make from that class will have all that baked-in.
Codealong: Write a basic class
I like dogs. Here's what a Dog class definition might look like in JavaScript:
The Dog
class is a collection of variables and methods. The constructor
method is a special method JavaScript executes when a new dog is created. We will talk about this shortly. The variables in this class are this.name
and this.age
. The method in this class is barkHello
.
Instantiating objects from classes
By defining the dog class we now know the structure that each of our dogs will have. It's like we have made a new variable type! But how do we make new dogs, or new variables of this type? We call our class name as a function with the new
keyword:
This will create a new object according to our Dog class specification. When that happens, JavaScript runs our constructor
method. Our constructor
method sets the name of this dog to 'Gracie' and sets her age to 8 years old.
So, what's this this
keyword? When we make objects from this class, the this
keyword inside each object refers to that specific object. This allows each object made from a class to maintain its own copies of variables.
this
can be a strange programming paradigm. Imagine that you are an object. You represent thethis.
If you wanted to access your arm, it would be this.arm. Your name would probably be stored in this.name. This lets each object made from a class keep reference to its own data and function members. Not every 'Person' has the same name so we want individual People to maintain their own names. Thethis
keyword is each objects' identity.
Let's make a couple more dogs so we can see the differences in our dog objects:
Each dog has its own name and age. Let's have them say hello:
Let's add some more code to better see what is going on behind the scenes.
Class (static) vs Instance members
In our Dog
class, we have variables attached to the this
property that exist independently for each object that's created. These are called instance variables. Each object instance has its own copies of these variables and they can vary across objects. We can also attach variables to the class itself so that there's one single thing that exists for an entire class. These go by different names in different languages but in JavaScript are called static variables.
Suppose we want to keep a tally of how many dogs we have running around in our app. We could put a copy of the tally in each dog object but that's not efficient: we would be duplicating a value in memory multiple times and we would have to update the value in every dog object in order to keep it accurate. It's much better if we can store it once in the class. That way, each dog object can access it but we only need to store it and set it in one place.
There is a nice, clean way to do this but we must use a very new feature called "class fields syntax" which simplifies the adding of member variables to a class as well as giving us the ability to easily add static variables. This is an experimental feature and is not supported in all browsers as of 2019 but when we develop using React (or any build chain that involves the Babel transpiler) we can have our newer-syntax JavaScript "transpiled" into older JavaScript that does run in older browsers.
Let's update our class to add a totalDogs
variable to the class. Let's also add a line that increments this value inside our constructor
method. And just for fun, let's add another line to our barkHello
method that references this total:
Now when we create a new dog, the constructor
method increments the totalDogs
counter which is stored in the Dog class itself. We can access the value stored in Dog.totalDogs
inside our script and each dog object can access it from their own functions.
Inheritance
Another amazing feature of classes and one of the central aspects of Object Oriented Programming is the idea of having one class inherit functionality and basic structure from another class. Using this idea, you can organize your classes from general to specific where "child" classes that inherit functionality from "parent" classes can refine and extend that parent's behaviors.
For example, we have a Dog
class and all dogs bark but not all dogs can fetch. We could add this functionality to the base Dog
class but since not all dogs fetch this doesn't make good design sense. What we should do is use the Dog
class as our parent or super class and then create a child or sub class that has this more specific behavior. Let's make a Retriever
class:
The extends
keyword is the special syntax here. We say that Retriever extends Dog
and this tells JavaScript that any object made from the Retriever
class will also have all the data and behavior from the Dog
class. There is also a new function in our constructor. Calling super()
invokes the constructor from the parent but does all of its operations for the new object we are instantiating. Let's try it out:
So we can see that the subclass dog, an instance of the Retriever class, has not only the new fetch() method but also the barkHello() method from the Dog class. But notice that any object instantiated from the Dog class does NOT have the fetch() method. That is because inheritance only works downward. Superclasses pass members down to subclasses, not the other way around.
Principles of Object Oriented Programming
There are three main aspects of OOP that people cite as the main advantages. Sometimes this question comes up in interviews so now that we have seen the machanics of OOP in JavaScript let's see how they make up the paradigm of OOP:
Encapsulation
That is a big, fancy word that means that your data and functions inside a class are protected and scoped to that class. Each object encapsulates its own data attributes and functions and because they are inside of an object, they are protected from global variable accesses. It also adds the benefit of organization because a class only needs to contain things relevant to the class itself. Classes are generally self-contained, modular and reusable - which are all good things in programming.
When we add a static variable to a class that can only be accessed through that class, that is a perfect example of the benefit of encapsulation. When we attach the data that comes in via the constructor to the individual objects we instantiate, that is also one of the virtues of encapsulation.
Inheritance
As we showed above, classes can incorporate behavior from other classes by inheriting from them using the extends
keyword. This leads to a lot of code savings. If we need more specific or additional functionality in a class we can simply extend that class and not need to rewrite anything. Most large software solutions, including Node and the DOM built into the browser, are built as a big collection of general-to-specific classes.
Polymorphism
A term meaning "many shapes", polymorphism refers to the ability of a function or even a whole object to take on different behaviors depending on which class is being called. Though the name of a function can stay the same as it is inherited to other classes, those subclasses can override that function with new or extended behavior. The code calling that method calls it the same as it would any other time but it will do different things depending on what object it was called on, hence "many shapes".
Exercise: Create Your Own JavaScript Class
Now that we've seen a dog-based example of JavaScript classes, let's make one a bit more sophisticated. Create a BankAccount
class.
Bank accounts should be created with the
accountType
property (like "savings" or "checking").Bank accounts should have a class-level (static) variable tracking the total amount of money in all accounts.
Each account should keep track of it's current
balance
.Each account should have access to a
deposit
and awithdraw
method.Each account should start with a
balance
set to zero.
Things to think about:
Any starting values for variables should be set in the
constructor
methodStatic variables are declared using the
static
keyword inside the class but outside any methodsInstance variables are declared inside the
constructor
methodDoes your
constructor
method need to accept any parameters?
Bonus: Start each account with an additional overdraftFees
property that starts at zero. If a call to withdraw
ends with the balance
below zero then overdraftFees
should be incremented by twenty.
Bonus bonus: Create a ChildBankAccount
class that overrides the withdraw
method to eliminate the overdraft penalty fee and simply disallow the withdrawal if there aren't enough funds.
Closing Thoughts
A Class is a pre-defined structure that contains attributes and methods that are grouped together logically.
An Object is an instance of a Class structure. A class could be thought of as a house blueprint and an object as a house built from the blueprint.
Classes are defined via a method call. Classes contain an
constructor
method that takes in parameters to be assigned to member variables (properties) of an Object.Instance variables contain data types declared in the class but defined in each object.
Static variables and methods contain data and actions that live on the class itself.
The
this
keyword lets us distinguish between variables that exist in different objects and refer to the object we are currently in.
Last updated