All About Objects
We've discussed most basic data types in Javascript, as well as ways we can manipulate them using control flow structures like if-else
statements or loops. But we haven't discussed one of the most fundamental data types in Javascript: the object.
Every non-primitive in Javascript is an object. This includes arrays and functions. It doesn't include numbers, strings, booleans, or undefined types. For more information on primitives see the MDN documentation on data structures in Javascript.
Objects in Javascript are very similar to the data structures known as hashmaps or dictionaries in other programming languages.
Objects have keys and values. The key is used to access a particular value in an object in the same way as an array.
// each entry in an object is a key-value pair
let myObject = {
"cats": "cute",
"dogs": "cool",
}
// we access values the same way as in an array
console.log(myObject["cats"])
// we can also access them using . syntax
console.log(myObject.cats)
// we assign new keys the same way we access them
myObject["birds"] = "smelly"
// you can loop through keys in an object using for in loops
for (key in myObject) {
console.log(`${key} are ${myObject[key]}`)
}
One of the main uses of objects is to make it easy to pass data between functions, especially when returning multiple values.
function analyseAnimals(animalData) {
let total = 0
let count = 0
for (animal in animalData){
total += animalData[animal]
count += 1
}
return {
"total": total
"average": total / count
}
}
const myPets = {
"cat": 1,
"dog": 1,
"rabbit": 3,
"fish": 24,
"parrot": 1
}
const data = analyseAnimals(myPets)
console.log(`Total Pets: ${data["total"]}; Average # of each pet: ${data["average"]}`)
Within the methods of an object, this
is used to refer to the object, and lets an object access its own properties. Note that this can get complicated but we cover only the usual case here.
const counter = {
value: 0,
reset: () => {
this.value = 0
},
increment: () => {
this.value += 1
},
}
console.log(counter.value)
counter.increment()
counter.increment()
console.log(counter.value)
counter.reset()
console.log(counter.value)
Objects can store any data type, just like an array. You can do some tricks with objects by exploiting this property; for example they can be used to simplify complex if-else statements:
// incomplete example of using objects to set up calculator functions
// if statement version
if (operation == '+') add(a,b)
else if (operation == '-') subtract(a,b)
else if (operation == '/') divide(a,b)
// object version
let operations = {'+': add, '-': subtract, '/': divide}
console.log(operations[operation](a,b))
One thing that's notable about objects is that they're not copied into functions (technically, they're not passed by value). Instead, they pass a reference to themselves. This means that objects passed into a function can be modified. For example:
const normalise = function (object) {
object.name = object.name.toUpperCase()
object.description = object.description.toUpperCase()
}
// const objects can have their values modified, but the object they reference can't be re-assigned
const myObject = {
"name": "My cool object",
"description": "This object is incredibly cool."
}
normalise(myObject)
console.log(myObject)
Whether or not modifying objects that are passed into functions is good practice or not is controversial and a major difference between functional and object-oriented styles of Javascript.
In the real world, this is usually determined by the problem you're solving rather than programming philosophy.
Exercises
- Given an array of numbers, return an object containing the frequencies of each number. For example, for the array
[1,1,2,5,5]
, you should return{1: 2, 2: 1, 5: 2}
. - (Leetcode #1) Given two arrays of numbers and a target number, return the indexes of one number from each array that sum to the target as an array of two numbers. For example, given
[1,2,3,4,5]
and[1,2,5,7,8]
with a target of7
, you should return the array[1,2]
, representing2+5
. setInterval(function, delay)
is a built in function which lets us run a given function repeatedly. You can also useclearInterval(interval)
to stop a previously-declared interval. Create a timer object with three methods:start
,stop
, andreset
. It should also have the propertytime
. Whenstart
is called, the timer should start counting untilstop
is called. Whenreset
is called, the timer should reset to zero.
Classes and Inheritance
In addition to being able to use objects as a way to easily store a lot of data, we can also use them as objects proper, as seen in object-oriented languages like C# or Java.
Every object in Javascript has a type, as we covered in our very first class. It's possible to create our own named types with the class keyword!
// classes use TitleCase by convention
class Book {
constructor(title, description, author) {
this.title = title
this.description = description
this.author = author
}
}
// we call the constructor function using new CLASSNAME
const myFavorite = new Book(
"Love in the Time of Cholera",
"In their youth, Florentino Ariza and Fermina Daza fall passionately in love. When Fermina eventually chooses to marry a wealthy, well-born doctor, Florentino is heartbroken, but he is a romantic. As he rises in his business career he whiles away the years in 622 affairs—yet he reserves his heart for Fermina. Her husband dies at last, and Florentino purposefully attends the funeral. Fifty years, nine months, and four days after he first declared his love for Fermina, he will do so again.",
"Gabriel Garcia Marquez"
)
// it's just a regular object
console.log(myFavorite.title)
// ...but it has the type Book
console.log(typeof myFavorite)
// by default, JS classes inherit from Object
console.log(myFavorite instanceof Object)
Pay attention to the last line of this code example. How can myFavorite
be both a Book
and an Object
? To answer that we need to understand inheritance.
Inheritance is a feature where child objects inherit properties from their parent objects. For example, in the below diagram, there are 4 classes: Polygon, Triangle, Rectangle, and Square.
Triangle and Rectangle inherit from Polygon directly, while Square inherits indirectly, via Rectangle. This is because Square is a special case of Rectangle.
How can we represent this in Javascript? Let's add three properties to our classes: width
, height
, and the area()
method.
class Polygon {
constructor (width, height) {
this.width = width
this.height = height
}
area () {
// we can't do this without a more complex representation
return undefined
}
}
class Triangle extends Polygon {
constructor (width, height) {
// super calls the parent's constructor and sets up this
super(width, height)
}
area() {
return (this.width * this.height) / 2
}
}
class Rectangle extends Polygon {
constructor (width, height) {
super(width, height)
}
area() {
return this.width * this.height
}
}
class Square extends Rectangle {
constructor (size) {
super(size,size)
}
area () {
return this.width * this.width
}
}
// you can use your own methods to play with this example!
// try typeof, instanceof, and calling the area() function on different types of object
There are two important things to note here. First is that the constructor
function in a child class will overwrite the constructor
in a parent class. However, we can still access the parent's constructor via the super
method, and in fact we need to call this method to get this
set up correctly. super
must also be called before any further modifications to this
in the child class's constructor.
Exercises
- Add in a
Shape
parent class. This should be a parent ofPolygon
. - Add in a
Circle
class as a child ofShape
. - Make the
Circle
class set itself up correctly. Add anarea
function toCircle
which will calculate the area correctly asMath.pow(Math.PI * radius)
.
Assignment
Write a quiz game where the player has to select from 4 choices (e.g. a,b,c,d). Each question needs to store the question, the possible answers, and the correct answer as an object. Note that the easiest way to do this is to use the quick way to make arrays and objects:
let listOfQuestions = [
{
// question body
}
]
At the end of the quiz, the player should be given their score and the option to play again. Please write 3 or more questions.
Hint: you probably want to write some functions for extracting information from your list of questions.
Extensions
These are optional but highly recommended if you want to reinforce your knowledge of object-oriented Javascript.
- Make a
Question
class with aconstructor
method, which allows us to create new questions by callingnew Question()
. - Make a
Quiz
class which takes a list ofQuestion
objects, adds them into a list, and keeps track of which questions have been asked. WhennextQuestion()
is called on theQuiz
, it should supply the next question until all questions are exhausted. It should also keep track of the player's current score. - Make the order of the questions in the quiz random by using
Math.random()
. - Add an additional type of
Question
to the quiz. This should be calledOpenQuestion
and allow for a free-text response. If the free text reply is correct, add a point. Rename the originalQuestion
class toMultipleChoiceQuestion
and make a newQuestion
class which is a parent of bothOpenQuestion
andMultipleChoiceQuestion
.