- Implement models of real-world entities using JavaScript objects.
- Differentiate between "property" and "method" definitions.
- Modify object properties.
- Access objects by reference.
- Fork and clone this repository. FAQ
- Create a new branch,
training
, for your work. - Checkout to the
training
branch. - Install dependencies with
npm install
.
It is time to delve deeper into the fundamental structure that underlies almost every aspect of JavaScript programming: objects.
You might already be more familiar with objects than you think, as JavaScript embraces them. Many components of the language are actually objects internally, and even those that are not, like strings or numbers, can still exhibit object-like behavior in certain situations.
JavaScript has only seven fundamental data types, with six of them being primitive data types:
- string
- number
- boolean
- null
- undefined
- symbol
With the seventh type, objects, we unlock more complex possibilities in our code. JavaScript objects can be used to represent real-world entities such as a basketball or a dog, or they can be used to construct the data structures that power the web.
At their core, JavaScript objects act as containers that hold related data and functionality. Despite their seemingly simple nature, objects possess remarkable power in practice. You have already been harnessing the power of objects, but now it is time to understand their mechanics and create your own!
Objects can be assigned to variables just like any JavaScript type. We use curly braces, {}
, to designate an object literal:
let spaceship = {}; // spaceship is an empty object
We fill an object with unordered data. This data is organized into key-value pairs. A key is like a variable name that points to a location in memory that holds a value.
A key’s value can be of any data type in the language including functions or other objects.
We make a key-value pair by writing the key’s name, or identifier, followed by a colon and then the value. We separate each key-value pair in an object literal with a comma (,). Keys are strings, but when we have a key that does not have any special characters in it, JavaScript allows us to omit the quotation marks:
// An object literal with two key-value pairs
let spaceship = {
'Fuel Type': 'diesel',
color: 'silver'
};
The spaceship
object has two properties Fuel Type
and color
. 'Fuel Type'
has quotation marks because it contains a space character.
Open ./exercises/fasterSpaceship.js and follow the instructions.
There are two ways to access an object's property. Let's explore the first way: dot notation (.
).
You have already used dot notation to access properties and methods of built-in objects and data instances:
'hello'.length;
With property dot notation, we write the object’s name, followed by the dot operator and then the property name (key):
let spaceship = {
homePlanet: 'Earth',
color: 'silver'
};
spaceship.homePlanet;
spaceship.color;
If we try to access a property that does not exist on that object, undefined
will be returned.
spaceship.favoriteIcecream;
Let’s get some more practice using dot notation on an object!
Open ./exercises/countTheCrew.js and follow the instructions.
The second way to access a key's value is by using bracket notation ([ ]
).
You have already used bracket notation when indexing an array:
const abc = ['A', 'B', 'C'];
console.log(abc[0]);
To use bracket notation to access an object’s property, we pass in the property name (key) as a string.
We must use bracket notation when accessing keys that have numbers, spaces, or special characters in them. Without bracket notation in these situations, our code would throw an error.
let spaceship = {
'Fuel Type': 'Turbo Fuel',
'Active Duty': true,
homePlanet: 'Earth',
numCrew: 5
};
//try logging the following options to the console:
spaceship['Active Duty'];
spaceship['Fuel Type'];
spaceship['numCrew'];
spaceship['!!!!!!!!!!!!!!!'];
spaceship.'Fuel Type';
spaceship.Fuel Type;
With bracket notation you can also use a variable inside the brackets to select the keys of an object. This can be especially helpful when working with functions:
let returnAnyProp = (objectName, propName) => objectName[propName];
returnAnyProp(spaceship, 'homePlanet');
If we tried to write our returnAnyProp()
function with dot notation (objectName.propName) the computer would look for a key of 'propName'
on our object and not the value of the propName
parameter.
Open ./exercises/secretMission.js and follow the instructions.
Once we've defined an object, we're not stuck with all the properties we initially wrote. Objects are mutable, which means we can update them after creating them.
To add new key-value pair to an object or change an existing property, we can use either dot notation (.
) or bracket notation ([]
) along with the assignment operator (=
).
Here's how we can perform property assignment:
One of two things can happen with property assignment:
- If the property already exists on the object, the new value will replace the old value.
- If there was no property with that name, a new property will be added to the object.
Please note that while we can't reassign an object declared with const, we can still mutate it by adding new properties or changing existing ones.
const spaceship = {type: 'shuttle'};
spaceship = {type: 'alien'};
spaceship.type = 'alien';
spaceship.speed = 'Mach 5';
You can delete a property from an object with the delete
operator.
const spaceship = {
'Fuel Type': 'Turbo Fuel',
homePlanet: 'Earth',
mission: 'Explore the universe'
};
delete spaceship.mission;
console.log(spaceship);
Open ./exercises/spaceshipUpgrade.js and follow the instructions.
Methods in JavaScript refer to functions that are stored within objects. They define the actions that an object can perform. In contrast, properties represent the characteristics or data that an object possesses.
You might already be familiar with object methods without realizing it. For example, the console
object in JavaScript provides a method called .log()
for printing messages to the console. Similarly, the Math
object includes a method called .floor()
for rounding numbers down to the nearest integer.
To define methods in JavaScript objects, we use key-value pairs separated by colons. The key represents the name of the method, while the value is an anonymous function expression.
By using methods, we can make objects in JavaScript more dynamic and functional. They allow objects to perform specific actions and provide a way to organize and encapsulate related functionality within the object itself.
const alienShip = {
invade: function () {
console.log('Hello! We have come to dominate your planet. Instead of Earth, it shall be called New Xaculon.')
}
};
With the new method syntax introduced in ES6 we can omit the colon and the function
keyword.
const alienShip = {
invade () {
console.log('Hello! We have come to dominate your planet. Instead of Earth, it shall be called New Xaculon.')
}
};
Object methods are invoked by appending the object’s name with the dot operator followed by the method name and parentheses:
alienShip.invade();
Open ./exercises/retreat.js and follow the instructions.
In programming, objects can be nested, which means that an object can have another object as one of its properties. This allows us to organize and structure our data hierarchically.
For example, imagine a spaceship
object. Inside this object, we can have a crew
object that represents the crew members on the spaceship. Each crew member is an object with properties like name
and degree
. We can also nest other objects within the spaceship
, such as a telescope
object or details about the spaceship's computers in a nanoelectronics
object.
By nesting objects, we can create complex data structures that represent real-world scenarios and the relationships between different entities.
const spaceship = {
telescope: {
yearBuilt: 2018,
model: '91031-XLT',
focalLength: 2032
},
crew: {
captain: {
name: 'Sandra',
degree: 'Computer Engineering',
encourageTeam() { console.log('We got this!') }
}
},
engine: {
model: 'Nimbus2000'
},
nanoelectronics: {
computer: {
terabytes: 100,
monitors: 'HD'
},
'back-up': {
battery: 'Lithium',
terabytes: 50
}
}
};
When working with nested objects, we can chain operators together to access properties at different levels. To do this, we need to choose the appropriate operator for each layer of the nested structure.
To make it easier, imagine yourself as the computer and evaluate each expression from left to right. This approach helps break down the operations and make them more manageable.
spaceship.nanoelectronics['back-up'].battery;
In the preceding code:
- First the computer evaluates
spaceship.nanoelectronics
, which results in an object containing theback-up
andcomputer
objects. - We accessed the
back-up
object by appending['back-up']
. - The
back-up
object has abattery
property, accessed with.battery
which returned the value stored there:'Lithium'
Open ./exercises/onBoard.js and follow the instructions.
When working with objects in JavaScript, they are passed by reference. This means that when we pass an object variable as an argument to a function, the parameter name inside the function refers to the same object in memory. Consequently, any changes made to the object's properties within the function will permanently modify the original object, even if it is assigned to a const
variable.
const spaceship = {
homePlanet : 'Earth',
color : 'silver'
};
let paintIt = obj => {
obj.color = 'glorious gold'
};
paintIt(spaceship);
console.log(spaceship.color)
Let's take another look at reasigning objects by refference:
//passing by value
let apple = "fruit"
let peach = apple
console.log(`apple is a ${apple}, and peach is a ${peach}`)
peach = "summer fruit"
console.log(`apple is a ${apple}, and peach is a ${peach}`)
//passing by reference
let apple = {type: "fruit"}
let peach = apple
console.log(apple,peach)
peach.type = "summer fruit"
console.log(apple,peach)
-
First we assign the value
"fruit"
to the variableapple
and then assign the value ofapple
to the variablepeach
. Bothapple
andpeach
now hold the same string value"fruit"
. -
We reassign the value of
peach
to"summer fruit"
. This reassignment does not affect the value ofapple
, as they are separate variables. -
We create an object with a property
type
set to"fruit"
, and assign it to the variableapple
. Then, we assign the reference of theapple
object to the variablepeach
. Bothapple
andpeach
now reference the same object in memory. Theconsole.log()
statement prints the values ofapple
andpeach
, which display the object's contents, resulting in the output:{type: "fruit"}
(twice). -
Finaly, we modify the
type
property of thepeach
object to"summer fruit"
. Sinceapple
andpeach
reference the same object, the modification affects both variables. Theconsole.log()
statement prints the updated values ofapple
andpeach
, which display the modified object's contents, resulting in the output:{type: "summer fruit"}
(twice).
To summarize, the code demonstrates how assigning variables works for primitive types like strings and objects. Assigning a primitive type creates independent copies of the value, while assigning an object creates a reference to the same object in memory. Modifying an object through one variable will affect all variables that reference that object.
Our function paintIt()
permanently changed the color of our spaceship
object. However, reassignment of the spaceship
variable wouldn’t work in the same way:
let apple = {type: "fruit"}
let peach = {...apple}
console.log(apple,peach)
peach.type = "summer fruit"
console.log(apple,peach)
//in a function
let spaceship = {
homePlanet : 'Earth',
color : 'red'
};
let tryReassignment = obj => {
obj = {
identified : false,
'transport type' : 'flying'
}
console.log(obj)
};
tryReassignment(spaceship)
console.log(spaceship)
Let’s look at what happened in the code example:
-
We declared a
spaceship
object usinglet
. This allowed us to reassign it to a new object with properties likeidentified
andtransport type
without any issues. -
However, when we passed the
spaceship
object into a function designed to reassign the object, the reassignment didn't have any effect (even though printing the object usingconsole.log()
gave us the expected result). -
The reason for this is that when we passed
spaceship
into the function, the function's parameter (obj
) became a reference to the memory location of thespaceship
object, not to thespaceship
variable itself. The function has no knowledge of thespaceship
variable. -
So when we performed the reassignment inside the function, the
obj
variable started referring to a new object{'identified': false, 'transport type': 'flying'}
, while thespaceship
variable remained unchanged.
Open ./exercises/energise.js and follow the instructions.
Loops are programming tools that repeat a block of code until a condition is met. We learned how to iterate through arrays using their numerical indexing, but the key-value pairs in objects aren’t ordered! JavaScript has given us an alternative solution for iterating through objects with the for...in syntax .
for...in
will execute a given block of code for each property in an object.
let spaceship = {
crew: {
captain: {
name: 'Lily',
degree: 'Computer Engineering',
cheerTeam() { console.log('You got this!') }
},
'chief officer': {
name: 'Dan',
degree: 'Aerospace Engineering',
agree() { console.log('I agree, captain!') }
},
medic: {
name: 'Clementine',
degree: 'Physics',
announce() { console.log(`Jets on!`) } },
translator: {
name: 'Shauna',
degree: 'Conservation Science',
powerFuel() { console.log('The tank is full!') }
}
}
};
// for...in
for (let crewMember in spaceship.crew) {
console.log(`${crewMember}: ${spaceship.crew[crewMember].name}`);
}
Our for...in
will iterate through each element of the spaceship.crew
object. In each iteration, the variable crewMember
is set to one of spaceship.crew
‘s keys, enabling us to log a list of crew members’ role and name
.
Open ./exercises/crewInfo.js and follow the instructions.