3. Classes
Follow along with code examples here!
Table of Contents:
Key Concepts
The methods of objects created by factory functions are recreated for each instance, wasting memory.
Classes provide a blueprint for creating objects with a shared interface.
The
instanceofoperator can be used to check if a given object is derived from a given class.The
constructor()method is invoked when thenew Class()syntax is used.Inside the
constructor(), the value ofthisis the new instance object.
Own properties are properties defined directly on an object (as opposed to those inherited from the class)
Intro: Reviewing OOP, Encapsulation, Factory Functions, and Interfaces
In the last lecture, we learned about encapsulation - bundling data and methods that act on that data into an object. We learned how to use closures to hide internal data and restrict access to them.
With encapsulation, we can re-use factory functions like makeFriendsManager to create multiple objects that look alike: each friends manager instance has a username and getFriends() and addFriends() methods.
We often refer to encapsulated objects as "interfaces" for the data that they manage.
An interface is a "shared boundary" where two or more components of a system can interact and exchange information. For example, your keyboard, mouse/trackpad, and screen together are the interface that you use to interact with your computer. Similarly, the methods of an encapsulated object are the interface that the caller of those methods can use to interact with the object's data.
Interfaces do not expose the inner details of the tool/machine/program that the user is operating — they instead provide well-defined and controlled access points for the user to operate it.
In summary, separation of concerns is achieved in OOP by identifying the features of an application and encapsulating the data and methods for those features in objects to create interfaces for each feature.
Factory Functions Waste Memory
The instances reuben and maya definitely have the same behavior. But do they share that behavior? That is, are the methods reuben.addFriend and maya.addFriend really the same function in memory?
Printing out the objects themselves shows us two identical-seeming objects:
But looking at the methods themselves, we can see that they are not referencing the same function in memory.
Each time the factory function is invoked, a brand new object is made and the methods are recreated as well. This is a waste of memory!
To address this memory issue, in 2016 classes were introduced to JavaScript. Let's see how they work.
Classes

A class is similar to a factory function in that it can be used to create objects that all have the same interface (the same set of properties and methods).
Q: Suppose we wanted to create a class to represent users. What would the default properties be? What methods would be shared by each instance?
The
Userclass would have a constructor function for making aUserinstance with properties likeusername,email, andpasswordThe
Userclass might have methods likechangeUsernameorsetPassword
Class Definition and new
newIn JavaScript, it starts with the class keyword, an uppercase name, and curly braces. Like this:
With a class definition, we can create new instances of that class ben and carmen using the new keyword.
Note: Even though Person is treated like a function (we invoke it), you must use the new keyword when making an instance (you'll get an error if you don't)
Instanceof
We can use the instanceof operator (kind of like the typeof operator) to see if an object is derived from the given class.
Setting Properties With A Constructor
We can add properties to the objects created by a class in two ways:
Define public class fields within the class body.
Define a
constructor()that accepts values when the instance is initialized.
As you can see, each instance of Person has the properties friends, name, and age. However, only name and age were set by the constructor while the friends property was hardcoded to []. Here's how it works:
constructoris a "reserved" method name. When thenew Class()syntax is used, JavaScript will look to see if the class has aconstructor()and will automatically execute it.Inside of a
constructor(), thethiskeyword references the new instance object being created.
Public class fields are added to every instance of a class.
thisis not needed to define a public class field.
All of the properties in this example are "public" and the objects are mutable.
Optional Constructor Parameters & Overwriting Public Class Fields
Let's say we want to have the option to add some initial friends when initializing a new Person. There are many ways to do this but the common pattern is to simply create a public field with a hardcoded default value and then overwrite it in the constructor if a value is provided.
Defining Instance Methods
Here's where classes start to shine. We can add methods that all instances share by defining them inside the class body without commas between them. Inside a method, the this keyword will refer to whichever instance is invoking the method.
"Own Properties" and Prototypes
In JavaScript, a property of an object is considered "own properties" if that property is defined directly on the object. For instances of a given class, only the public class fields and properties set in the constructor are "own properties". For the Person class, these would be friends, name, and age.
You can see which properties are "own properties" held by an object using the Object.getOwnPropertyNames() static method:
So, where are the methods addFriend and greet stored if not in the instances? And how can those instances still invoke those methods?
In JavaScript when we create a class, the methods of the class are stored as own properties of the Class.prototype object. For example, we can see the properties owned by the Person.prototype:
Every Person instance has access to that Person.prototype object. It is described as "the prototype of a Person instance".
We can get the prototype of an instance using the Object.getPrototypeOf() static method:
As you can see, the prototype of the ada instance is the object Person.prototype
Array Prototype
We can see the same thing is true for arrays. The "own properties" of an array are the indexes of the array and the length property. Array methods are stored in the prototype object Array.prototype.
This is often why you will see methods like arr.push() written as Array.prototype.push.
Why This Matters
All of this shows us the main benefit of classes compared to factory functions: the methods are truly shared between instances because they are owned by their prototype, not the instance.
Quiz!
Can you spot the mistake(s) with the code below?
Q: Answer
The following mistakes are made:
constis used instead ofclassto define theAnimalclassWe don't need the
=to create the classThe
ownersproperty with the default value doesn't needthisThe
constructorfunction should be written like this:constructor () {}without the:and=>We don't need a comma to separate the methods
makeSoundshould usethis.soundWhen creating an instance of
Animal, thenewkeyword should be used.
Challenge
Create a class called FoodItem. Every instance of FoodItem should have the following properties and methods
name— the name of the itemprice- the price of the item in US dollarsweight- the weight of the itemgetPricePerPound()- returns the price / pound of the item
For example, I should be able to use this FoodItem class like so
Now, create a second class called ShoppingCart. Every instance of ShoppingCart should have the following properties and methods:
items— an array that starts empty. It should holdFoodIteminstances.addItem(FoodItem)— takes in aFoodIteminstance and adds it to theitemsarray.getTotalPrice()- calculates the total price of allFoodItemsin theitemsarray
For example, I should be able to use this ShoppingCart class like so
Summary
A class defines a type of object with shared methods and properties
It has a constructor function for defining the default properties that every instance of that class (objects of that type) will have.
All instances of that class inherit the class' methods.
Classes are defined using the
classkeywordInstances of a class are created using the
newkeyword and the class constructor.When used in a constructor function,
thispoints to the newly created objectWhen used in a method,
thispoints to the object invoking the method
Last updated