Introduction to Object Oriented Programming¶
Many of you are already familiar with the ideas of Object-Oriented Programming (OOP), but some of you are not. If you are familiar, it's not bad to review and see how OOP is done in JS.
General Idea¶
In OOP, an object is a kind of value (data structure) that can have two important characteristics:
State: This is information describing the object. For example, a ball object could be have a state like: being color red, have a radius of 12 inches, and have polka dots. Even state that changes over time, such as its x,y,z location in space.
Behavior: This is the information describing the actions of the object. For instance, a ball object can change its location or bounce.
In order to create specific objects, we need to describe a class of objects and then create some instances of the class. (Note that these are different from the HTML classes!) Imagine a class as a general kind of thing, and objects are the specific instances of that kind. Here are some examples of classes and objects.
- Class: Ball
- Objects:
- b1: Red, has a radius of 12, has polka dots, bounces
- b2: Blue, has a radius of 5, does not have polka dots, bounces
- Class: Wellesley College Student
- Objects:
- stu1: Beatrice, Class of 2019, MAS major, says "hello!"
- stu2: Jackie, Class of 2018, biology major, runs to lab
- stu3: Kate, Class of 2020, English major, sings
Programming Classes and Objects¶
In discussing how to implement Object-Oriented Programming in JavaScript, we have a choice. In 2015, the JavaScript language was expanded to include specialized syntax to support classes and objects, even though the language always had OOP. That's pretty long ago, now, so it's probably no longer worth learning the old syntax. However, there are some ideas in there, so we will review it.
We will focus with the new syntax because it is simpler and easier to understand. In fact, that's its purpose: to be a better syntax for an existing implementation. This is sometimes called syntactic sugar. So, for that reason, we'll introduce the new syntax first, and defer the older syntax that means the same thing.
Modern Syntax¶
In this presentation, I've followed the Mozilla Developer's Network web docs on Classes, but that document isn't intended for novices. Nevertheless, you're welcome to read it if you want more depth and rigor.
Let's describe our ball objects using the class syntax:
class Ball {
constructor(color, radius, hasPolkaDots) {
this.ballColor = color;
this.ballRadius = radius;
this.doesThisBallhavePolkaDots = hasPolkaDots;
}
getColor() {
return this.ballColor;
}
setColor(color) {
this.ballColor = color;
}
bounce() {
console.log('Booiiiinng. Ball is bouncing.');
}
}
Some observations:
- a class has a special function called a constructor which initializes the instance variables of each new object (instance of the class). The constructor is automatically invoked whenever you create a new instance.
- a class can have any number of methods. Here we have three:
getColor,setColorandbounce.
Let's see how our code would use this class definition:
// examples of creating two instances of the class
var b1 = new Ball("Red", 12, true);
var b2 = new Ball("Blue", 5, false);
// examples of invoking a method
console.log(b1.getColor()); // "Red"
b1.setColor("Green");
console.log(b1.getColor()); // now "Green"
// example of retrieving an instance variable
// it would be better to define a method to do this
console.log(b2.ballRadius); // 5
More observations:
- A method is invoked by giving the object (or a variable containing the object) a dot and the name of the method, along with parentheses around any arguments to the method. As with function invocation, there are always parentheses, even if there are no arguments to the method. Indeed, that's because a method is just a special kind of function. This is exactly the same syntax that we learned with methods on objects like dates, strings and arrays, so you're already familiar with this.
- We use the
newoperator when creating an instance of a class. (Again, this is the same as we learned earlier when we used objects.) Thenewoperator creates the new, empty, object, binds it tothisand then invokes the constructor. The constructor can then initialize the object by using the magic keywordthis. - Similarly, methods can refer to the object using
thisin their code. Thethisvariable above is always the object inb1but we could easily invoke a method onb2.
You can see that code in action using the ball modern demo. Note that the web page will be blank. View the page source to see the JS code, and open the JavaScript console to see what was printed to the console.
Type in the names of the two variables, b1 and b2 to see how
they are displayed.
There are other features of the new syntax that we won't go into now, but we may look at later in the course.
This this Keyword¶
You will notice that both the constructor and the methods often
referred to a "variable" named this. Actually, it's not really a
normal variable, this is a special keyword, but you can think of it
like a variable, but it's a variable whose value is the current
object.
Specifically, that means that this in a constructor means the
brand-new object that was just created by the new operator and which
the constructor is initializing. For example, this is one of the
"ball" objects (b1 or b2) that we created and the constructor is
now setting its radius or color.
Similarly, in the methods, such as getColor or setColor, the
keyword this is the object that we are invoking the method on. For
example, when we execute the following code:
b1.getColor();
When the getColor method executes, this is bound to the same
object as b1.
Other languages have this same idea. Java also calls the keyword
this while Python calls it self.
Classic Syntax¶
Let's revisit our ball example but without the helpful syntactic sugar. Remember that the ideas and implementation is the same.
function Ball(color, radius, hasPolkaDots) {
this.ballColor = color;
this.ballRadius = radius;
this.doesThisBallhavePolkaDots = hasPolkaDots;
};
Ball.prototype.getColor = function() {
return this.ballColor;
};
Ball.prototype.setColor = function(color) {
this.ballColor = color;
};
Ball.prototype.bounce = function () {
console.log('currently bouncing');
};
This defines a constructor (the Ball function) and three methods, all
defined as functions stored in the prototype property of the
constructor. Very different from the new syntax, but means the same thing.
Interestingly, the code to use the Ball class is exactly the same:
// examples of creating two instances of the class
var b1 = new Ball("Red", 12, true);
var b2 = new Ball("Blue", 5, false);
// examples of invoking a method
console.log(b1.getColor()); // "Red"
b1.setColor("Green");
console.log(b1.getColor()); // "Green"
// example of retrieving an instance variable
// it would be better to define a method to do this
console.log(b2.ballRadius); // 5
b1.bounce(); // Booiiiiinng. Ball is bouncing
b2.bounce(); // Booiiiiinng. Ball is bouncing
You can see that code in action using the ball classic demo. Again that the web page will be blank. View the page source to see the JS code, and open the JavaScript console to see what was printed to the console.
Let's break the code into sections. The first 5 lines of this code are:

The class name is Ball, as indicated by the box and purple in the
picture. By convention, class names are always capitalized. When creating
a Ball using new, it needs to be given a color, a radius, and whether or
not it has polka dots, as shown by the green. The code with the keyword
this indicates that you are storing the green parameters
(color, radius, hasPolkaDots) to the instance variables (ballColor,
ballRadius, doesThisBallHavePolkaDots). The instance variables describe
the properties or state of the object, as described earlier in this
reading.
Comparing this to the modern syntax, we see that the capitalized function plays the role of both class and constructor function.
Now let's turn to methods.

Just as instance variables describe state, methods describe the
behavior of the object. Here, the name of the method is bounce as
indicated by the box and the purple. When a ball bounces, it logs
"Booiiiiinng. Ball is bouncing." When you create new methods, you always
preface the function with the name of the class (in this case, Ball) and
the key word prototype.
Comparing this to the modern syntax, we see that methods are defined as properties of the prototype property of the class (constructor function). Every instance gets the class as its prototype, so putting methods as properties of the prototype means that every instance gets those methods.
Here are some examples:

After you create the Ball class, you can create specific instances of
Ball objects. The first two lines of code create b1 and b2, each with its
own characteristics. Notice that when you create brand new objects, you
must use the keyword new. The next three lines of code
accesses the different instance variables. For example, b1 is
purple. Finally, the last two lines of code access the method of the Ball
class. Both b1 and b2 have the method bounce, so they will behave the
same way. Notice that when you access methods, you must use parentheses
at the end! (as shown by the orange).
Furthermore, if you compare the old syntax with the new, you'll see that using OOP (creating instances and invoking methods) are exactly the same between the two. The new syntax just makes it easier to describe the class, its constructor and methods.
Prototype¶
The main reason to introduce the old syntax is to make explicit the
role of prototype. The prototype of a JavaScript class is a special
object that all instances of the class share. It's a place where
methods for that class are stored and looked up at run-time.
Because the prototype exists at run-time and can be dynamically modified, JavaScript's OOP system is very dynamic (much more so than, say, Java's). That means that, in principle, we can define new methods (or even re-define existing methods) while our program is running.
However, that kind of dynamic behavior is rare. In practice, JavaScript's OOP features are almost always used the same way that they are in Java or Python: defined once when the program compiles and loads, and treated as static from then on.
Later in the course, we'll see some additional uses of the prototype.
Exercises¶
- Below is the Ball code in JSfiddle to explore. Open up JSfiddle and the console, and try creating new objects, accessing instance variables, and calling methods. Afterwards, try creating new instance variables and methods to your liking. For example, define methods to get and set the x, y location of the ball.
- Create your own Person class. Add instance variables and methods of your choices.
A Database Class¶
We'll use OOP in CoffeeRun in several ways, but let's start with using them to store data. The database will be a "key-value" store, meaning that the data (value) will be looked up via a key. Examples might be looking up
- your student information via your "Student ID" (e.g. C12345678)
- course information via a key like "CS204"
- faculty info via an email address like "sanderso@wellesley.edu"
There are lots of industrial-strength key-value databases. One is Redis which is used by Potluck.
The API (Application-Programmer Interface) to our class will be like this:
class DataStore {
constructor() {
}
add (key, val) {
}
get (key) {
}
getAll () {
}
remove (key) {
}
}
I have omitted the code for these methods, above, because we don't need to know that information in order to use the class. That notion of abstraction gives the implementor more freedom, and it's an important aspect of OOP.
DataStore Usage¶
Here are some examples of using this DataStore class:
var ex1 = new DataStore();
ex1.add('harry', {name: 'Harry Potter',
hair: 'black',
house: 'Gryffindor'})
ex1.add('ron', {name: 'Ron Weasley',
hair: 'red',
house: 'Gryffindor'})
The values are these JS objects and the keys are simple strings like
"harry". The add method adds a key-value pair to the database.
More examples:
var h1 = ex1.get('harry');
console.log(h1.name);
The get method allows us to retrieve the information from the
database.
For completeness, we'll add two more methods:
getAllwhich returns a JS object (dictionary) of all the data, andremove(key)which removes an entry from the database.
API Application Programming Interface¶
The set of methods provided by a class is an example of a API: Application Programming Interface. An API is a way for two pieces of software to talk to each other.
It's very similar to the idea of interfaces, which we talked about in the context of modules. That is, when we break a program into parts (divide-conquer-glue, like you learned in CS 111), we have to decide how the various parts will communicate: function names, arguments and their types, etc.
Usually, one side is the "boss" or "client" and the other provides
some service to the client. For example, CoffeeRun's main module will
be the client, and the DataStore will provide a service for it,
namely to store stuff (probably coffee orders).
There are many important reasons for this way of thinking. Some are:
- abstraction: the client doesn't need to know about the
implementation details of the
DataStoreand other modules. - modularity: the client can easily swap in a different module that has the same interface but maybe different behavior.
An API is often compared to a contract: the object agrees to provide services as long as the client uses the constructor and methods in the documented way.
This notion of seamlessly replacing a class with another class that has the same methods is a really powerful one which we will see examples of in later versions of CoffeeRun.
DataStore Implementation¶
Now, let's look at how this datastore might be implemented. One very simple option is to use a JS object (a dictionary), since that already allows key-value pairs.
class DataStore {
// instance variables
data = {};
constructor() {
console.log('running the DataStore function')
this.data = {};
}
add (key, val) {
this.data[key] = val;
}
get (key) {
return this.data[key];
}
remove (key) {
delete this.data[key];
}
getAll () {
return this.data;
}
}
Please read the implementation. Here are a few notes:
- it is optional, but a common practice, to put a list of the instance
variables at the top of the class, before the
constructor. Here, we just have one, calleddata. It will hold our JS dictionary. - The
constructorjust initializes thedatato an empty dictionary - The
addmethod just stores a key/value pair - The
getmethod just retrieves a value - The
removemethod deletes a key from a dictionary. We didn't see this feature of JS before, but it does what the Python dictionarypop()method does. - The
getAllmethod returns the dictionary. But see note below.
Note that the getAll method isn't ideal, because it exposes our
implementation, and might allow a client to overly rely on it, or even
muck with the representation. But this implementation is simple, so
we'll use it for now.
Even though it was simple, the DataStore class is important because
of abstraction and modularity. It provides an API and using it
means that we can change the implementation if want to, and the
client code continues to work. Indeed, we will do exactly that in
future versions of CoffeeRun.
The Truck Class¶
Before we conclude, let's look at one more class. This class
represents the orders received by a single food truck. It basically
just keeps track of the pending orders. It uses a DataStore to
actually store the data, but it provides a few more methods. Let's see
the abstraction:
class Truck {
constructor (truckId, db) {
}
createOrder (order) {
}
deliverOrder (customerId) {
}
printOrders() {
}
}
So, it has just one constructor and three methods:
- The constructor takes an ID of the truck (a string, useful for printing) and a database object for it to use for storage.
- a
createOrdermethod that takes a bunch of information, represented as an object/dictionary and stores it. This "order" object will come from the data collected in the CoffeeRun order form. - a
deliverOrdermethod that implements the behavior of delivering the order and removing the order from the database - a
printOrdersmethod that prints all the pending orders, mostly for debugging purposes, but could be useful in its own right.
An interesting observation is that the database is passed in to the constructor. For example:
let ds1 = new DataStore();
let truck1 = new Truck('mater', ds1);
We could alternatively have the database be created by the
constructor, but that means that in order to change our database
representation, we would have to modify the source code of the Truck
module, which experience shows is not a good design. So, while the
constructor argument list is a little more complicated this way, the
result is better, more modular coding. (If you're curious, this
technique is called dependency
injection in
software engineering.)
Truck Implementation¶
We will look more carefully at the implementation of the Truck class
later, but here are the basics. Note that there is a bug in the last
method. We will learn how to fix that bug later.
class Truck {
// instance variables
truckId = null;
db = null;
constructor (truckId, db) {
this.truckId = truckId;
this.db = db;
}
createOrder (order) {
console.log('Adding order for ' + order.emailAddress);
this.db.add(order.emailAddress, order);
}
deliverOrder (customerId) {
console.log('Delivering order for ' + customerId);
this.db.remove(customerId);
}
// This method has a bug!
printOrders () {
console.log('Truck #' + this.truckId + ' has pending orders:');
// the following works; `this` has the correct value
let allOrders = this.db.getAll();
let customerIdArray = Object.keys(allOrders);
console.log(customerIdArray.length+' orders');
customerIdArray.forEach(function (id) {
// the following fails because `this` has the wrong value
let order = this.db.get(id);
console.log(order);
});
}
}
You'll notice that many of these methods are essentially trivial. Does
this mean the Truck class is a waste of effort? No! As our program
becomes more complicated, we might find there are more things that
need to happen when an order is created or delivered, and so having
these methods gives us a place to put that code, while still allowing
us the abstraction and modularity of a class.
Summary¶
This was a relatively short reading, but dense with ideas. Object-Oriented Programming is important both historically and in modern programming. We'll play with these ideas more in class, and we'll use OOP in several assignments later this semester.