Core JavaScript

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();
1
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');
  },
};
1
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();
1
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
1
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
1
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']
1
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;
}
1
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`
}
1
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`.
1

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.
1
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'] },
  ],
};
1
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
1
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
    }
  }
}
1
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
Last Updated: 5/31/2023, 8:15:38 PM