Javascript Objects and Classes
Like Python, Java and C++, Javascript is an object oriented language but the way that objects work in Javascript is very different from any of those languages.
Javascript Objects
Javascript objects are prototype based, meaning that every object
has another object as its prototype (except for the base object
called Object
which has a prototype of null
). Classes in Java
would just be objects in Javascript - there is no difference between
a class and an instance.
The other distinct feature of Javascript objects is that they can be modified dynamically - new members and methods can be added after the object has been created.
There is a class system overlaid on these objects but it's use is optional. We can just create an object and then add data and methods to it. Here's an example:
const myCar = new Object()
myCar.make = 'Holden'
myCar.model = 'Astra'
myCar.year = 2009
myCar.describe = function() {
return 'Car: ' + this.make + ', ' + this.model + ', ' + this.year
}
The first line creates a new instance of Object
which is the top
of the class hierarchy in Javascript. The
remaining lines add three properties and a method describe
that
returns a description of the car.
Note that the method uses the variable name this
to refer to
the instance (similar to Java and like self
in Python). Properties
can be accessed
using the dot notation myCar.year
, or alternately using
square brackets myCar['year']
.
Note that this is then quite similar to a dictionary in Python or a HasMap in Java - a collection of properties and values - and in fact objects in Javascript can be thought of as a kind of associative array of keys and values, some of which can be functions. This is more obvious if we use an alternate syntax for making the object:
const myCar = {
make: 'Holden',
model: 'Astra',
year: 2009,
describe: function() {
return 'Car: ' + this.make + ', ' + this.model + ', ' + this.year;
}
}
Objects are often used as pure data structures in Javascript code without adding additional methods. This is very apparent in the rise in popularity of the JSON format (Javascript Object Notation) for exchange of data over the internet. JSON documents hold data rather than code and translate directly to Javascript objects once parsed.
If we are using objects as associative arrays it is useful to be able to
get the property names of an object at run-time. We can do this using the
static method Object.keys()
which takes an object and returns an array
of the property names. So, Object.keys(myCar)
would return the array
[ 'make', 'model', 'year', 'describe' ]
. Note that the describe
method
is just another property name that happens to have a function as its value.
Another useful thing to do to an associative array is to iterate over
the properties and values, we can do this in Javscript with the for...in
loop:
for(const key in myCar) {
console.log(key, myCar[key])
}
Inside the loop we use the square bracket notation to access the object property using the key loop variable.
You can also use the in
keyword to check whether a given property is
present in an object. The following code will insert a new key-value
pair into an object if it isn't already present and throws an error otherwise:
const insertIfNotPresent = (object, key, value) => {
if (!(key in object)) {
object[key] = value;
} else {
throw(new Error(`${key} is already present in object`));
}
}
If you want to remove a property from an object you can use the delete
operator
as in this example:
delete myCar.year
Classes
There are a number of ways of making objects in Javascript but the Class system
is the easiest to understand coming from other languages. A class in
Javascript is way of defining a template for an object prototype. We can
then use the new
operator to create an object instance from a class
definition. Here's our car example as a class definition:
class Car {
constructor(make, model, year) {
this.make = make;
this.model = model;
this.year = year;
}
describe() {
return 'Car: ' + this.make + ', ' + this.model + ', ' + this.year
}
}
const myCar = new Car('Holden', 'Astra', 2009);
The class definition is a way of defining how to make a new object and the methods that go along with it. Making an object like this is essentially the same as the previous examples. This style makes sense if the reason for having an object in your code is to represent some abstraction in your application. If it's just for creating new data structures, you probably would use the other methods.
Classes can inherit from each other, so we can do the usual object-oriented example of a class hierarchy.
class MotorVehicle {
constructor(year, wheels) {
this.year = year;
this.wheels = wheels;
}
getWheels() {
return this.wheels;
}
}
class Car extends MotorVehicle {
constructor(year, colour) {
super(year, 4); // all cars have four wheels
this.colour = colour;
}
describe() {
return 'Car: ' + this.year + ', ' + this.wheels + ', ' + this.colour;
}
}
const newCar = new Car(2009, 'white');
newCar.describe()
wheels = newCar.getWheels();
Here the two classes define their own constructor. The Car
class constructor
first calls the function super
which invokes the constructor of its parent
class MotorVehicle
with the given arguments. Since cars always have four
wheels, we hard-code this value into the call to super. The car class
defines its own property colour
. When we create an instance of the Car
class we can call methods defined on Car
or on MotorVehicle
which will be
inherited.
Inheritance like this can happen with objects created in other ways, it's just harder to establish the chain of inheritance than it is with class definitions.
Object Methods and this
We've seen a few ways to create objects and define methods on those objects. In the first example I used the function keyword to define the describe method but what if I had used an arrow function?
const myCar = new Object()
myCar.make = 'Holden'
myCar.model = 'Astra'
myCar.year = 2009
myCar.describe = () => {
return 'Car: ' + this.make + ', ' + this.model + ', ' + this.year
}
It turns out that this is one place where we can't interchange these two ways
of writing functions. Arrow functions do not have access to the this
keyword
and so can't operate on the object instance. In the example, the values
of this.make
etc would be undefined
since this
would refer to the global
object rather than the car.
If you are using a class definition, then the methods are defined with
property names and so will become regular functions. If you are adding methods
to functions dynamically or defining them as literal objects as in this example,
then you need to be sure to use the function keyword if you want to use this
.
Built In Objects
Javascript comes with a large collection of built in object prototypes that
you can make use of in your code. Consult
a good reference
to get a
list of all of them. You'll see many of these in examples in later parts
of this text. One useful example is the Date
prototype:
const today = new Date();
const birthday = new Date("Dec 25, 1993");
//show the date
console.log(today.toString());
// show the date in GMT timezone
console.log(today.toGMTString());