4.2 Objects
# JavaScript Objects
The Object
datatype is the base object that is used to create all other non-primitive datatypes in JavaScript. While this may sound quite abstract, you will actually be creating many Objects of your own.
There are a several ways to create objects. Here are a few of the more common ones.
//the object constructor
let myObj1 = new Object(); //creates an empty object
//an object literal
let myObj2 = {}; //creates an empty object
//a Factory style function that creates objects when called with 'new' keyword
function DoIt() {} //instead of returning undefined, it returns an object of type DoIt
let myObj3 = new DoIt();
//The class syntax to define an object plus calling its constructor
class myApp {
constructor() {}
}
let myObj4 = myApp();
2
3
4
5
6
7
8
9
10
11
12
13
14
15
The most common is the object literal syntax, where you are literally writing out what the object contains. Here is an object literal with 5 properties - 3 properties are just values (like primitive variables) and 2 are methods (like function expressions).
let potter = {
age: 3189, //numeric property
isWizard: true, //boolean property
name: 'Harry', //string property
speak: function () {
console.log(`You're a wizard ${potter.name}.`);
// potter.name is 'Harry'
},
levitate: function () {
console.log('Levi-oh-sa');
},
};
2
3
4
5
6
7
8
9
10
11
12
# Prototypes
Every type of Object has a prototype
. A prototype
is a special kind of an object that contains all the methods that will be shared by all Objects of that type.
JavaScript has something called the prototype chain
, which is how inheritance
works in JavaScript. Each one of the Object prototypes will have a connection to the prototype object belonging to it's parent object. At the top of the chain is the prototype
of the Object
object.
As an example, look at the toString()
method. When you create an Array (opens new window), there is no method in Array called valueof
. However, you can write the following and no error occurs.
let letters = new Array('a', 'e', 'i', 'o', 'u');
letters.valueof();
2
valueof reference (opens new window)
This works because of the prototype chain
.
We are calling the method Array()
. The Array
function has a prototype object. All the methods that you would call on your array, like map
or sort
or filter
are inside the Array.prototype
object. The prototype object of Array.prototype
is the Object.prototype
object. (this is the prototype chain)
When the line letters.valueof()
is run, the JavaScript engine looks inside of letters
for a method called valueof
. If it is not found then JS looks inside Array.prototype
for a method called valueof
. If the method is not found there, then JS looks inside Object.prototype
for the method. Since Object.prototype.valueof
does exist it can be run.
The prototype
of Object.prototype
is null
. Once null
is reached in the search through the prototype chain, then JS knows that it can safely say that an error has occured.
We will talk more about the prototype chain in the future. For, now, it is enough if you understand that this is how inheritance of methods work in JavaScript and that every object has a prototype.
# Square Brackets vs Dot Notation
The first way we access any property or method in an object is with dot notation. That means putting a period between each object name and property name.
let obj = {
name: 'Bubba',
age: 44,
};
obj.name; // has the value 'Bubba'
obj.age; // has the value 44
2
3
4
5
6
There is an alternative syntax that uses square brackets.
let obj = {
name: 'Bubba',
age: 44,
};
obj['name']; // has the value 'Bubba'
obj['age']; // has the value 44
2
3
4
5
6
Note the quotation marks around the property names. All Object property names (for our purposes) will be Strings.
So, why the two approaches? - With the square brackets we can put a variable inside the brackets instead of a string.
let obj = {
name: 'Bubba',
age: 44,
};
let n = 'name';
let a = 'age';
obj[n]; // has the value 'Bubba'
obj[a]; // has the value 44
obj.n; // this would fail because JS would look for obj.n or obj['n']
obj.a; // this would fail because JS would look for obj.a or obj['a']
2
3
4
5
6
7
8
9
10
11
So, if you are trying to dynamically update an object or access it's properties inside a function and you need the function to work with any object or any property then you need to use variables that contain those references. With the square brackets we can reference any property.
function doUpdate(someObj, someProp, someVal) {
//this function can update the value of any property inside any object
someObj[someProp] = someVal;
}
2
3
4
# Checking for Property Existence
As a best practice, when you are going to change an object by updating the value of a property, deleting a property, or adding a new property, then you should check to see if that property already exists.
There are two ways that we can check for the existence of a property on any object. We can use the in
operator or the hasOwnProperty()
method. The in
operator is the simplest and shortest to write.
let obj = {
depth: 8,
width: 34,
ref: 'AB393620',
};
if ('depth' in obj) {
//there IS a depth property in the Object `obj`
}
if (obj.hasOwnProperty('width')) {
//there IS a width property in the Object `obj`
}
2
3
4
5
6
7
8
9
10
11
# Deleting Properties from Objects
When you are done with a property and want to get rid of it there are two things we can do. First, to permanently delete the property, we use the delete
keyword.
delete obj['ref']; //removes the property `ref` from the Object `obj`.
Now, if you only want to temporarily remove the value, but still keep the property to use later with a new value, we can just update its value to null
.
obj.myprop = null;
obj['otherProp'] = null;
//both these properties still exist but their value is now null.
2
3
# Nested Loops for Complex Objects
There will be times when you have a complex object that has arrays nested inside of arrays. When you need to loop through all of the elements at every level, we will use a nested loop.
Take this data as our example:
var pets = {
animals: [
{ dogs: ['Woody', 'Roxy', 'Rane'] },
{ cats: ['Willow', 'Jesse Pink-kitty', 'Bob'] },
],
};
2
3
4
5
6
In this object, animals is an array of objects. Each of the objects inside of animals has a property which is an array.
We want to be able to write out all these pet names dynamically in a format that looks like this:
dogs
Woody
Roxy
Rane
cats
Willow
Jesse Pink-kitty
Bob
2
3
4
5
6
7
8
So, a nested loop means a loop inside of a loop.
NOTE: In the outer loop we are declaring two variables a
and numAnimals
in the first part of the loop conditions. This is not something special for nested loops. You can do this with any for...loop
.
let animals = pets.animals; //our first outer array
for (let a = 0, numAnimals = animals.length; a < numAnimals; a++) {
for (let prop in animals[a]) {
//this will only loop once and get us the property name
console.log(prop);
//now get all the pet names for this prop (dogs or cats)
for (let i = 0, numPets = animals[a][prop].length; i < numPets; i++) {
console.log('\t', animals[a][prop][i]);
//add the \t to indent one tab
}
}
}
2
3
4
5
6
7
8
9
10
11
12
Here we used a loop inside a loop inside a loop. The second loop was necessary because our array was hidden behind a property name. If we only had an array of arrays then two loops would be all that was required.
# What to do this week
TODO
Things to do before next week.
- Read all the content from
Modules 4.1, 4.2, and 5.1
. - Re-Read
Module 5.1
- Review
Module 5.1
- Come up with some questions about the content in
Module 5.1
- Continue working on the Hybrid Exercises
- Submit Hybrid 4