# Day 1: JS Objects

## JS Objects

### Objects in Review

Let's visit a site most of you will probably be familiar with, [Amazon](https://www.amazon.com). If we type something to search for, you may notice all the results have similar properties. Things like, *price*, *title*, *reviews*, *Prime eligibility* and a *picture*.

In programming, we need a way to contain logic and data about things in the real world and represent them in our programs. An effective way to do this is with *objects*.

In JavaScript, **objects are collections of properties(key-value pairs)**. We can add, remove, or change these properties as we please. The simplest way to create an object is by using **object literal notation**.

```javascript
const car = {
  make: 'Honda',
  model: 'Civic',
  year: 1997, // Generally, there's no comma after the last pair!
};
```

> "make" is the key, while "Honda" is the value

> Objects must have both a key and a value - neither can be empty.

Objects are a complex data type - sometimes referred to as a dictionary/hash/map.

* They are a collection of key-value pairs called properties.
* Key-value pairs are separated by commas.
* The keys which we explicitly state when defining a property are analogous to our array indexes. They are how we access the associated value (more below).

In the above example, the variable `car` points to an object literal. This particular object has 3 properties: `make`, `model` and `year`.

We could store this same information in an array like this...

```javascript
const car = ['Honda', 'Civic', 1997];
```

What advantages might there be in storing `car` as an object?

#### Interacting with Objects

**Create**

We already saved a sample object to a `car` variable. We did this using **object literal notation**.

```javascript
const car = {
  make: 'Honda',
  model: 'Civic',
  year: 1997,
  'tire-type': 'Goodyear',
  // NOTE: Keys with a "-" or space in their name must be surrounded by quotation marks.
  // NOTE: You should use camelCase in JS, but sometimes you need to work with hyphens in JSON objects, if they were generated from a non-JavaScript API.
};
```

**Read**

To access object properties, we use either dot `.property` or bracket `['property']` notation.

```javascript
console.log( car.make )
console.log( car['make'] )

// What happens when we try to access a property yet to be defined?
console.log( car.owner )

// NOTE: When accessing properties whose keys have a "-" or space in them, you must use bracket notation.
console.log( car['tire-type'] )

// NOTE: When accessing properties with a variable, you must also use bracket notation
function(carProp) {
  car[carProp]
}
```

Dot notation is much more common than bracket notation. Why might that be?

**Update**

Call on the object property just like we did when reading it, and use the assignment operator `=` followed by its new value.

```javascript
car.year = 2003;
// or
car['year'] = 2003;
```

We can also create brand new properties for an object after it's initialized using this method.

```javascript
// Now our car has a smell property equal to "Leathery Boot". We did not initially declare this property.
car.smell = 'Leathery Boot';

console.log(car);
```

**Delete**

If you want to delete an object property entirely, use the `delete` keyword.

* This deletes the whole key-value pair, not just the value.
* You won't do this often.

```javascript
delete car.smell;
```

**Iterating through an Object**

Like arrays, you can use a loop to iterate through an object. Say we want to print out all of an object's keys...

This is called a `for in` loop

```javascript
// Iterate through object keys
for (key in car) {
  console.log(key);
}
```

> Knowing this, how could we go about getting all the values in an object?

JavaScript objects also have native methods that take care of this for us...

```javascript
// .keys()
Object.keys(car);
// .values()
Object.values(car);
// key-value pairs
Object.entries(car);
```

### Methods

Methods are functions that are attached to some object.

```javascript
// Our car now has a drive method...
const car = {
  make: 'Honda',
  model: 'Civic',
  color: 'red',
  drive: function () {
    console.log('vroom vroom');
  },
  gears: ['Reverse', 'Neutral', '1', '2', '3', '4'],
  engine: {
    horsepower: '6 horses',
    pistons: 12,
    fast: true,
    furious: false,
  },
  // Methods can take arguments
  gps: function (location) {
    console.log(`Beep boop, driving to ${location}`);
  },
};

// We can run the car's two methods like so...
car.drive();
car.gps('neverland');
```

Checkout our awesome souped-up car! With methods as part of our JavaScript toolbox, we now have an interface with which we can interact with our objects.

> We've only scratched the surface for objects. We'll dive a bit deeper into them later. If you're looking for more on the power of objects and functions, we recommend reading [The Secret Life of JS Objects](http://eloquentjavascript.net/06_object.html) chapter in Eloquent JS

## Context

### What is Context?

In JavaScript, context tells us where functions are invoked.

In short, the **context is the object that a function is attached to**. We'll see that context can change under certain circumstances.

Every time a JavaScript function is called, a context is determined / set. That context is always an object, and can be referenced in the function definition using a special keyword in JS, `this`.

We use `this` similar to the way we use pronouns in natural languages like English and French. Say we write:

```
John bites an apple. The apple tastes good
```

We can also say it another way:

```
John bites an apple. This tastes good
```

What does `this` refer to?\` The apple.

In a similar manner, we use the `this` keyword as a replacement for the subject in question.

#### `this` in an Object

Here's an example of the most common way context is determined for a function. When a method is called on an object, that object becomes the context...

```javascript
const user = {
  fullName: 'James Reichard',
  sayName: function () {
    console.log(`My name is ${this.fullName}.`);
  },
};
user.sayName();
```

<details>

<summary><strong>What does <code>this</code> represent here?</strong></summary>

Here the object that the method is being called on is `user`

</details>

#### 'Getting' Properties using `this`

```javascript
const user = {
  fullName: 'James Reichard',
  favoriteFood: 'Rice pudding',
  sayName: function () {
    console.log(`My name is ${this.fullName}.`);
  },
  sayHello: function () {
    console.log(
      `Hi my name is ${this.fullName} and my favorite food is ${this.favoriteFood}.`
    );
  },
};

user.sayHello(); // for this function invocation, `this` is `user`
```

#### 'Setting' Properties using `this`

This feature allows not just 'getting' property info on objects, but also setting properties. Consider this example:

```javascript
const user = {
  userName: 'numbr1rawkr',
  isSignedIn: false,
  signIn: function () {
    this.isSignedIn = true;
  },
  signOut: function () {
    this.isSignedIn = false;
  },
};

user.signIn();
user.isSignedIn; // => true
user.signOut();
user.isSignedIn; // => false
```

*But what if we want more control?*

Because we've written a method to set the `isSignedIn` property, we can use that method to provide more control. For example... what if we wanted to check a user's password before letting them sign in?

```javascript
const user = {
  userName: 'numbr1rawkr',
  password: 'password1234',
  isSignedIn: false,
  signIn: function (pwd) {
    if (pwd === this.password) {
      this.isSignedIn = true;
    }
  },
  signOut: function () {
    this.isSignedIn = false;
  },
};

user.signIn('tacobell');
user.isSignedIn; // => false
user.signIn('password1234');
user.isSignedIn; // => true
user.signOut();
user.isSignedIn; // => false
```

#### 'Running' methods using `this`

We can also use `this` to reference and call other methods on the object.

```javascript
const user = {
  userName: "numbr1rawkr",
  password: "password1234",
  isSignedIn: false,
  signIn: function(pwd) {
    if(pwd === this.password) {
      this.isSignedIn = true
      this.greetUser()
    }
  },
  signOut: function() {
    this.isSignedIn = false
  },
  greetUser: function() {
    console.log(`Welcome back ${this.userName}!`)
  }
}

user.signIn("tacobell")
user.isSignedIn // => false
user.signIn("password1234")
// => Welcome back numbr1rawkr
user.isSignedIn // => true
user.signOut()
user.isSignedIn // => false
-
```

#### Default Context

When a function is called, but it's not a method on an object, and no context is otherwise assigned (see later sections), then the context is set to the default context. In a browser, the default context is the `window` object.

```javascript
function revealThis() {
  console.log(this);
}

revealThis();
```

#### A Rule of Thumb

In general, `this` is probably the **parent** or enclosing item (item being function or object)

Some exceptions are:

* In an event listener function, `this` is the thing where the event originated (such as the button that was clicked).
* In another callback function, in which case `this` is probably the `window`.
* When `.bind()` is used to change the context manually.

When in doubt, log it out...

```javascript
console.log(this);
```
