Objects in Javascript
Published: 2021-04-15 19:19:01

# **Notes**

## **The Object Type**

The **`object`** is a data structure that stores other data, similar to how an array stores elements.

- The object differs in that each **`value`** stores in an obj is associated with a **`key`**.

**The Object of My Affections**

In other programming languages, objects are referred to as, "dictionaries", "maps", or "associative arrays".

- Objects are indexed with **`keys`** instead of numbers.
- Order is not guaranteed within an Object.
- Objects are defined by using curly braces **`{}`**
- You can think of Objects as tables.

> Fun Fact: Objects are affectionately known as POJO's (Plain Old Javascript Objects)

**Setting Keys and Values**

```js
// here "color" is the key!
> car["color"] = "Blue";
"Blue"

> car["seats"] = 2;
2

// accessing our object at the key of color
> car["color"]
"Blue"

> car["seats"]
2

> car
{color: "Blue", seats: 2}
```

- We assign values to an object by defining the name of the key in brackets and assigning it to a value.

```js
> car
{color: "Blue", seats: 2}

> "color" in car;
true

> "model" in car;
false
```

- If we try to access a key that has not yet been assigned within an object we will output undefined.
- The **preferred method** for checking to see if an object exists at a key is to use the **`in`** operator.

**Using Variables as Keys**

```js
> car
{color: "Blue", seats: 2}

> let newVariable = "color";
undefined

> newVariable
"color"

> car[newVariable]
"Blue"

---

> car
{color: "Blue", seats: 2}

> newVariable
"weight"

// assigning a key value pair using a variable!
> car[newVariable] = 1000;
1000

> car
{color: "Blue", seats: 2, weight: 1000}
```

- It is useful to set a variable as a key, because variables can be re-assigned new values - this way we can quickly access different data and also create new key/value pairs.

## **Using Different Notations**

```js
> let dog = {};
undefined

> dog.bark = "Bowowowo";
"Bowowowowo"

> dog.bark
"Bowowowo"

> dog
{ bark: "Bowowowowo" }
```

- We can also use **dot notation** **"."** to access key/value pairs in an object.
  - One thing to note is that when using dot notation, we do not have to use string quotes as the key.

**Bracket Notation vs Dot Notation**

| **Dot**                                              | **Bracket**                                                |
| ---------------------------------------------------- | ---------------------------------------------------------- |
| Easier To Read                                       | You can use variables as keys!                             |
| Easier To Write b/c do not need Quotations.          | Okay to use variables and Strings that start with numbers. |
| Cannot access with Variables                         |                                                            |
| Keys cannot contain numbers as their first character |                                                            |

- **When accessing object keys**: Bracket notation needs to refer to that key in quotations, dot notation doesn't.
- **When accessing object keys via a variable**: Bracket notation can refer to that key w/o use of quotations, dot notation can't do this at all.

```js
let myDog = {};
myDog.name = "Fido";

let myKey = "name";
console.log(myDog); // prints `{name: "Fido"}`
console.log(myDog[myKey]); // prints `Fido`

console.log(myDog.myKey); // prints: undefined
```

- As illustrated above, the dot notation cannot access a varible key - since it takes a **literal** interpretation of the key.

**Putting it All Together**

You can put an object together in a single statement.

```js
let myDog = {
  name: "Fido",
  type: "Doge",
  age: 2,
  favoriteToys: ["bone", "ball"],
};
```

**Operator Precedence Revisited**

- The concept of Operator Precedence also applies to objects.
- There are two types of associativity:
  - **`Right Associativity`** : When code is evaluted right to left.
    ```js
    a = b = 1;
    ```
    - Since **assignment of variables** takes lowest precendence, we end up evaluating b = 1 first before a = b.
  - **`Left Associativity`** : When code is evaluated left to right.
    ```js
    let id = "header";
    let element = document.getElementById(id).value;
    ```
    - We first resolve the document variable, then use dot notation to retrive the getElementById function, we eval it's arguments, access it's value, and then retrieve assignment (the lowest precedence).

---

## **Iterating Through Objects**

Because objects store _**unordered**_ key-value pairs, we do not rely on indices to access values; instead we rely on our keys.

**A New Kind of For Loop**

```js
for (let variable in object) {
  statement;

  let obj = { name: "Rose", cats: 2 };
  for (let currentKey in obj) {
    console.log(currentKey);
    console.log(obj[currentKey]);
  }

  // prints out:
  // name
  // cats
  // Rose
  // 2
}
```

- We use a special syntax to iterate through each key of an object called a **`for-in loop`**.

**Methods vs Functions**

A **`Method`** is a function that _belongs_ to an object. Every method is a function, but _not_ every function is a method.

```js
myFunc is a function
myObject.myFunc is a method of the object myObject
myObject["myFunc"] is a method of the object myObject
```

- **Methods** are just a key-value pair where the **key is the function name and the value is the function definition**.

```js
let dog = {
  name: "Fido",
};

dog.bark = function () {
  console.log("bark bark!");
};

// this is the same thing as above just using Bracket Notation
dog["speak"] = function (string) {
  console.log("WOOF " + string + " WOOF!!!");
};

dog.bark(); // prints `bark bark!`
dog.speak("pizza"); // prints `WOOF pizza WOOF!!!`

let dog2 = {
  name: "Rover",

  bark: function () {
    console.log("bork bork!");
  },

  speak: function (string) {
    console.log("BORK " + string + " BORK!!!");
  },
};
// Notice that in the object above, we still separate the key-value pairs with commas.
// `bark` and `speak` are just keys with functions as values.

dog2.bark(); // prints `bork bork!`
dog2.speak("burrito"); // prints `BORK burrito BORK!!!`
```

- To invoke these methods we just need to specify which object is calling that method.

```js
myObject.methodName();
```

**Useful Object Methods**

- **`Object.keys()`** : A method that allows us to iterate through keys, it accepts an obj as the argument and returns an array of the keys.
- **`Object.values()`** : Method that accepts an object as the argument and returns an array of the values.

**Iterating through an Object's keys & values**

- **`Object.entries`** : Method that accepts an object as the argument and returns an array of the [key,value] pairs within.

```js
> Object.entries(cat)
[ [ 'name', 'Freyja' ], [ 'color', 'orange' ] ]
```

---

## **References vs Primitives**

**Primitives vs Objects**

So far we have learned about 6 different data types:

- **Primitive** : Boolean, Null, Undefined, Number, String.
- **Reference** : Object (Arrays are a type of object)
- Remember that **primitive** types are immutable!

**Immutabiity**

![pic of nums](https://github.com/bgoonz/Lambda/blob/master/WEEKS/wk2/0-notes/d4/mem2.png)

- When we reassign primitives we simply have our variable point elsewhere in memory.
- In a nutshell, **immutability** cannot change values in memory, but only reassign where our variables are pointing to.

**Mutabulity**

![img of mut](https://github.com/bgoonz/Lambda/blob/master/WEEKS/wk2/0-notes/d4/mem.png)

- If we change either cat1 or cat2, our computer memory will change because they are both pointing at the same memory location.

---

## **Rest and Spread**

**Using the Spread Operator and Rest Parameter Syntax**
**Accepting Arguments**

- Just keep in mind that function will still run even if it is not passed any arguments.
- Parameters will take just as many arguments they need even if more than enough are offered.
- We will encounter an error if there are not enough parameters ( > 0).

**Utilizing Rest Parameters**

- **`Rest Parameter Syntax`** : Allows us to capture all of a function's incoming arguments into an array.
- Only the last parameter can be a rest parameter.

**Utilizing Spread Syntax**

- **Spread Operator** : Allows us to break down a data type into the elements that make it up.
  - Takes a data type (i.e. array, obj) and spreads the values of that type where elements are expected.
  - Takes iterable data and spreads the elements of that type where arguments are expected.

```js
let numArray = [1, 2, 3];

// here we are taking `numArray` and *spreading* it into a new array where
// comma separated elements are expected to be
let moreNums = [...numArray, 4, 5, 6];

> moreNums
// => [1, 2, 3, 4, 5, 6]
```

**With Objects**

```js
let colors = { red: "scarlet", blue: "aquamarine" };
let newColors = { ...colors };

> newColors
// { red: "scarlet", blue: "aquamarine" };
```

```js
let colors = { red: "scarlet", blue: "aquamarine" };
let colors2 = { green: "forest", yellow: "sunflower" };

let moreColors = { ...colors, ...colors2 };

> moreColors
// {red: "scarlet", blue: "aquamarine", green: "forest", yellow: "sunflo
```

**Spreading Arguments**

```js
function speak(verb, noun) {
  return "I like to go " + verb + " with " + noun + ".";
}

const words = ["running", "Jet"];

console.log(speak("running", "Jet")); // => I like to go running with Jet.
console.log(speak(...words)); // => I like to go running with Jet.
```

---

## **Destructuring**

- **`Destructuring Syntax`** : Allows you to extract parts of an array or obj intro distinct variables.

```js
let numArray = [10, 20];

// here we are "unpacking" the array values into two separate variables
let [firstEl, secondEl] = numArray;

console.log(firstEl); //=> 10
console.log(secondEl); //=> 20
```

**Swapping Variables using destructuring**

```js
let num1 = 17;
let num2 = 3;

// this syntax will swap the values of the two variables
[num1, num2] = [num2, num1];

console.log(num1); // 3
console.log(num2); // 17
```

- One of the cool things we can do with destructuring is swap the values of two variables.

**Destructuring objects into variables**

- One of the most useful parts of destructuring is the ability to take apart and assign little slices of large objs to variables.

```js
let obj = { name: "Apples", breed: ["tabby", "short hair"] };
let { name, breed } = obj;

console.log(name); // "Apples"
console.log(breed); // ["tabby", "short hair"]
```

- **Aliased Object Destructuring** : When our variable does not have the same name as our object's keys.

```js
let obj = { apple: "red", banana: "yellow" };
let { apple: newApple, banana: newBanana } = obj;

console.log(newApple); // "red"
console.log(newBanana); // "yellow"
```

- Good rule of thumb to keep clarity in your code is to only destructure values from objects that are two levels deep.

```js
// the fname key is nested more than two levels deep
// (within bootcamp.instructor.fullName)
let bootcamp = {
  name: "Lambda",
  color: "red",
  instructor: {
    fullName: {
      fname: "Rose",
      lname: "K",
    },
  },
};

// this is hard to follow:
let {
  instructor: {
    fullName: { fname, lname },
  },
} = bootcamp;
console.log(fname, lname);

// this is much easier to read:
let { fname, lname } = bootcamp.instructor.fullName;
console.log(fname, lname);
```

**Destructuring and the Rest Pattern**

```js
let foods = ["pizza", "ramen", "sushi", "kale", "tacos"];

let [firstFood, secondFood, ...otherFoods] = foods;
console.log(firstFood); // => "pizza"
console.log(secondFood); // => "ramen"
console.log(otherFoods); // => ["sushi", "kale", "tacos"]
```

- Currently the rest pattern is only officially supported by JS when destructuring arrays.

```js
let { a, c, ...obj } = { a: 1, b: 2, c: 3, d: 4 };
console.log(a); // => 1
console.log(c); // => 3
console.log(obj); // => { b: 2, d: 4 }
```

---

## **Destructuring Parameters**

We can also destructure **incoming parameters** of a function.
This is very useful when we're passing objects around to different functions.

```js
let cat = { name: "Rupert", owner: "Curtis", weight: 10 };

// This unpacks the *owner* key out of any incoming object argument and
// assigns it to a owner parameter(variable)
function ownerName({ owner }) {
  console.log("This cat is owned by " + owner);
}

ownerName(cat);
```

```js
let bigCat = {
  name: "Jet",
  owner: { name: "Rose" },
  toys: ["ribbon"],
  siblings: { name: "Freyja", color: "orange", toys: ["mouse", "string"] },
};

// here we use *aliased* object destructuring to create a siblingToys variable
function toyFinder({ toys, siblings: { toys: siblingToys } }) {
  let allToys = toys.concat(siblingToys);
  return allToys;
}

console.log(toyFinder(bigCat)); // => ["ribbon", "mouse", "string"]
```

### Codepen Examples:

```js
// Objects
// major fundamental building block of JS
// allow us to group properties and values together
// order of properties does not matter

const dog = {
  // key value pairs
  name: 'Lola',
  breed: 'Yorkie',
  age: 2,
  color: 'light brown',
  favFood: 'beef gravy',
  // methods - functions that belong to an object
  eat: function(){
    return `${this.name} likes to eat ${this.favFood}`
    // you can do dog.name or dog.food
  }
}

console.log(dog); 
// printing objects in different editors and consoles may show "different" results
// the properties ay print in alphabetical order
// and sometimes methods may not print

// how can we access values in an object?
// 1. Dot notation -- see this a lot more often
// to the left of the dot is the object name and to the right of the dot is the key
// dog.age; --> this would return 2

console.log(dog.color);

//2. Bracket notation
// dog['age'] --> this would return 2

console.log(dog['color']);

// these notations work for key and values of string and numbers and etc
// but for methods use the dot notation

console.log(dog.eat());


// example - playing around with the method

const dog2 = {
  // key value pairs
  name: 'Lola',
  breed: 'Yorkie',
  age: 2,
  color: 'light brown',
  favFood: 'beef gravy',
  // methods - functions that belong to an object
  eat: function(food){
    return `${this.name} likes to eat ${food}`
    // you can do dog.name or dog.food
  }
}

console.log(dog2.eat('hotdogs'))

// nested objects

const dog = {
  name: 'Lola',
  breed: 'Yorkie',
  age: 2,
  favToy: {
    name: 'Mr Twinkle',
    type: 'plush toy',
    condition: 'fair',
    currentlyUsed: true
  },
  play: function(){
    return `${this.name} likes to play with ${this.favToy.name}`
    // another way to access favToy properties
    // this.favToy['name']
  }
}

console.log(dog.play());

// you can also nest objects into an array

const places = [
  // each object is accessed by index
  {city: 'NYC', country: 'United States', region: 'North America'}, // index 0
  {city: 'Paris', country: 'France', region: 'Europe'}, // index 1
  {city: 'Toronto', country: 'Canada', region: 'North America'}, // index 2
  // 2, --> adding other value types still makes this a valid array
  // 'a random string',
  // [0,1,2]
]

// how can we access index 2 city name
console.log(places[2].city);
console.log(places[2]['city']);// --> bracket notation would still work
// console.log(places[3]); --> this would bring out 2 because objects will not change the index

// nest an array into an object

const dog = {
  name: 'Lola',
  breed: 'Yorkie',
  age: 2,
  favToys: ['Mr Twinkle', 'Big Bone', 'Tennis Ball'],
  play: function(){
    return `${this.name} likes to play with ${this.favToys[0]}`
    // another way to access favToy properties
    // this.favToy['name']
  }
}

console.log(dog.play());

// examples with objects
const places = [
  // each object is accessed by index
  {city: 'NYC', country: 'United States', region: 'North America'}, // index 0
  {city: 'Paris', country: 'France', region: 'Europe'}, // index 1
  {city: 'Toronto', country: 'Canada', region: 'North America'}, // index 2
]

// how would I create a new array called North America and push all the objects in the North America region?

let northAmerica = [];

for(let i = 0; i < places.length; i++){
  if(places[i].region === 'North America'){
    northAmerica.push(places[i].city);
  }
}

console.log(northAmerica);

// create a function that adds a new object to the end of the array
// our function is going to take 4 parameters: array name, city, country, region
// it should return the array with the new object

// examples with objects
const places = [
  // each object is accessed by index
  {city: 'NYC', country: 'United States', region: 'North America'}, // index 0
  {city: 'Paris', country: 'France', region: 'Europe'}, // index 1
  {city: 'Toronto', country: 'Canada', region: 'North America'}, // index 2
]

function addCountry(array, city, country, region){
  array.push({city, country, region});
  return array;
}

// another way to do it
function addCountry(array, cityName, countryName, region){
  array.push({city: cityName, country: countryName, region: region});
   return array;
}

// another way to do it
function addCountry(array, city, country, region){
  let newCountry = {city, country, region}
  array.push(newCountry);
   return array;
}

console.log(addCountry(places, 'Mexico City', 'Mexico', 'North America'));


// Change values in an object

let city =  {
  name:'Mexico City',
  country: 'Mexico',
  region: 'North America'
}

city.name = 'Cabo'
console.log(city);

const places = [
  // each object is accessed by index
  {city: 'NYC', country: 'United States', region: 'North America'}, // index 0
  {city: 'Paris', country: 'France', region: 'Europe'}, // index 1
  {city: 'Toronto', country: 'Canada', region: 'North America'}, // index 2
]

places[0].city = 'Chicago';
console.log(places);

function changeCity(array, index, newCity){
  array[index].city = newCity;
  return array;
}

console.log(changeCity(places, 0, 'Miami'));



// Using functions to create objects
// examples make a pet object that has a name, species and well behaved (as a boolean) from a function

/*
{
  name: 'Lola',
  species: 'dog',
  well-behaved: false

*/

// the following functions are different ways to get this done:

function createPet(name,species, wellBehaved){
  let obj = {}
  obj = {name, species, wellBehaved}
  return obj
}

console.log(createPet('Lola', 'Yorkie', true));
// const lolaOne = createPet('Lola', 'Yorkie', true)
// console.log(lolaOne)

function createPet2(obj, name, species, wellBehaved){
  obj = {name, species, wellBehaved}
  return obj
}

// let myPet = {}
// myPet = createPet2(myPet, 'Lola', 'Yorkie', true);
// console.log(myPet);

function createPet3(name,species, wellBehaved){
  return {name,species, wellBehaved}
}
console.log(createPet3('Lola', 'Yorkie', true));
// const lolaThree = createPet3('Lola', 'Yorkie', true)
// console.log(lolaThree);

// pushing to an array
let petArray = []

function createPetArray(name,species, wellBehaved){
  petArray.push({name,species, wellBehaved})
}

createPetArray('Lola', 'Yorkie', true);
console.log(petArray)

// BREAKOUT
// create a function that will return an object of a person
// so it should have name, age, location (city), favorite color properties
// and a method that prints out a greeting like "Hi, my name is Gabby"

function createPerson(name, age,location, color){
  let greeting = () => { return `Hi, my name is ${name}`};
  return {name, age, location, color, greeting};
  // same as return {name: name, age: age, location: location, color: color, greeting};
}

// without a greeting variable
function createPerson(name, age,location, color){
  return {name, age, location, color, greeting: () => { return `Hi, my name is ${name}`}};
}

let newPerson = createPerson('Gabby', 27, 'NYC', 'blue');
console.log(newPerson);
console.log(newPerson.greeting());


// Review

// arrow functions

function thisIsAFunction(param1, param2){
  
}

// is the same as :

const thisIsAFunction = (param1,param2) => {
  
}


/// Making a website responsive 
// need a viewport meta tag in the head of your HTML doc 
// media queries 
// @media(max-width: 800px){
//    styles go in here
// }
// max width is for desktop first design -- scales down
// min width is mobile first -- scales up

// USE SCALABLE UNITS TO SIZE THINGS!!!!!!!!

// Accessibility 
// make sure you are using semantic HTML 
// make sure alt attributes are filled out


// Intro to JS 
// Variable - decleared using var, let or const 
// var - can be redecleared, can be updated, it is function scoped
// let - cannot be redecleared, can be updated, it is block scoped 
// const - cannot be redecleared, cannot be updated, it is block scoped 
// RULE OF THUMB - use const till you can't and then use let but stay away from var 

// functions 
function functionName(param1, param2){
  return param1 + param2;
}

// arguments happen when the function is invoked - the order we pass them in is the order in which they replace the parameters 
functionName(3, 4);
// 3 is going to take the place of param1 
// 4 is going to take the place of param2 

// if(conditional goes here - needs to be equated to true or false){
  // if the condition is true - this code will run 
  // code goes here
// }else if(another condition){
  // more code
//}else{ // else statements never have a condition they say "if nothing else is true pick me!"
// more code
//}

// anatomy of a loop 
// for loops are for iterating through data and then doing something with it usually based on some condition 
// first thing in a loop is where the loop starts this will almost always be 0
// condition - how long you want the loop to run 
// iteration - i++ is most common and says iterate by 1
// for(let i = 0; i < array.length; i++){
    // code here
//}

// ARRAYs
// order matters
// index starts at 0 
// const array =[0,1,2,3,4,5];
// when we use arrays we have access to array methods 🎉
// pay attention to if the method is mutable - you may want to copy the array if it is






```





---