Object basics
An object is a collection of related data and/or functionality. These usually consist of several variables and functions (which are called properties and methods when they are inside objects).
As with many things in JavaScript, creating an object often begins with defining and initializing a variable. Try entering the following line below the JavaScript code that's already in your file:
const person = {};
Congratulations, you've just created your first object. Job done! But this is an empty object, so we can't really do much with it. Let's update the JavaScript object in our file to look like this:
const person = {
name: ["Bob", "Smith"],
age: 32,
bio: function () {
console.log(`${this.name[0]} ${this.name[1]} is ${this.age} years old.`);
},
introduceSelf: function () {
console.log(`Hi! I'm ${this.name[0]}.`);
},
};
After saving and refreshing, try entering some of the following into the JavaScript console on your browser devtools:
person.name;
person.name[0];
person.age;
person.bio();
// "Bob Smith is 32 years old."
person.introduceSelf();
// "Hi! I'm Bob."
You have now got some data and functionality inside your object, and are now able to access them with some nice simple syntax!
So what is going on here? Well, an object is made up of multiple members, each of which has a name (e.g. name
and age
above), and a value (e.g. ['Bob', 'Smith']
and 32
). Each name/value pair must be separated by a comma, and the name and value in each case are separated by a colon. The syntax always follows this pattern:
const objectName = {
member1Name: member1Value,
member2Name: member2Value,
member3Name: member3Value,
};
The value of an object member can be pretty much anything — in our person object we've got a number, an array, and two functions. The first two items are data items, and are referred to as the object's properties. The last two items are functions that allow the object to do something with that data, and are referred to as the object's methods.
When the object's members are functions there's a simpler syntax. Instead of bio: function ()
we can write bio()
. Like this:
const person = {
name: ["Bob", "Smith"],
age: 32,
bio() {
console.log(`${this.name[0]} ${this.name[1]} is ${this.age} years old.`);
},
introduceSelf() {
console.log(`Hi! I'm ${this.name[0]}.`);
},
};
Dot notation
Above, you accessed the object's properties and methods using dot notation. The object name (person) acts as the namespace — it must be entered first to access anything inside the object. Next you write a dot, then the item you want to access — this can be the name of a simple property, an item of an array property, or a call to one of the object's methods, for example:
person.age;
person.bio();
Objects as object properties
An object property can itself be an object. For example, try changing the name member from:
const person = {
name: ["Bob", "Smith"],
};
to:
const person = {
name: {
first: "Bob",
last: "Smith",
},
// …
};
To access these items you just need to chain the extra step onto the end with another dot. Try these in the JS console:
person.name.first;
person.name.last;
Bracket notation
Bracket notation provides an alternative way to access object properties. Instead of using dot notation like this:
person.age;
person.name.first;
You can instead use square brackets:
person["age"];
person["name"]["first"];
This looks very similar to how you access the items in an array, and it is basically the same thing — instead of using an index number to select an item, you are using the name associated with each member's value. It is no wonder that objects are sometimes called associative arrays — they map strings to values in the same way that arrays map numbers to values.
Dot notation is generally preferred over bracket notation because it is more succinct and easier to read. However there are some cases where you have to use square brackets. For example, if an object property name is held in a variable, then you can't use dot notation to access the value, but you can access the value using bracket notation.
In the example below, the logProperty()
function can use person[propertyName]
to retrieve the value of the property named in propertyName
.
const person = {
name: ["Bob", "Smith"],
age: 32,
};
function logProperty(propertyName) {
console.log(person[propertyName]);
}
logProperty("name");
// ["Bob", "Smith"]
logProperty("age");
// 32
What is "this"?
You may have noticed something slightly strange in our methods. Look at this one for example:
introduceSelf() {
console.log(`Hi! I'm ${this.name[0]}.`);
}
You are probably wondering what "this" is. The this
keyword typically refers to the current object the code is being executed in. In the context of an object method, this
refers to the object that the method was called on.
Let's illustrate what we mean with a simplified pair of person objects:
const person1 = {
name: "Chris",
introduceSelf() {
console.log(`Hi! I'm ${this.name}.`);
},
};
const person2 = {
name: "Deepti",
introduceSelf() {
console.log(`Hi! I'm ${this.name}.`);
},
};
In this case, person1.introduceSelf()
outputs "Hi! I'm Chris."; person2.introduceSelf()
outputs "Hi! I'm Deepti." This happens because when the method is called, this
refers to the object on which the method is called, which allows the same method definition to work for multiple objects.
This isn't hugely useful when you are writing out object literals by hand, as using the object's name (person1
and person2
) leads to the exact same result, but it will be essential when we start using constructors to create more than one object from a single object definition, and that's the subject of the next section.
Introducing constructors
Using object literals is fine when you only need to create one object, but if you have to create more than one, as in the previous section, they're seriously inadequate. We have to write out the same code for every object we create, and if we want to change some properties of the object - like adding a height
property - then we have to remember to update every object.
We would like a way to define the "shape" of an object — the set of methods and the properties it can have — and then create as many objects as we like, just updating the values for the properties that are different.
The first version of this is just a function:
function createPerson(name) {
const obj = {};
obj.name = name;
obj.introduceSelf = function () {
console.log(`Hi! I'm ${this.name}.`);
};
return obj;
}
Now we can create as many objects as we like, reusing the definition:
const salva = createPerson("Salva");
salva.introduceSelf();
// "Hi! I'm Salva."
const frankie = createPerson("Frankie");
frankie.introduceSelf();
// "Hi! I'm Frankie."
This works fine but is a bit long-winded: we have to create an empty object, initialize it, and return it. A better way is to use a constructor. A constructor is just a function called using the new
keyword. When you call a constructor, it will:
- create a new object
- bind this to the new object, so you can refer to this in your constructor code
- run the code in the constructor
- return the new object.
Constructors, by convention, start with a capital letter and are named for the type of object they create. So we could rewrite our example like this:
function Person(name) {
this.name = name;
this.introduceSelf = function () {
console.log(`Hi! I'm ${this.name}.`);
};
}
To call Person()
as a constructor, we use new
:
const salva = new Person("Salva");
salva.introduceSelf();
// "Hi! I'm Salva."
const frankie = new Person("Frankie");
frankie.introduceSelf();
// "Hi! I'm Frankie."