# Introduction to Objects in Javascript

I am currently interviewing for Frontend Developer roles and I failed one of the interviews because I was not good at Object Oriented Javascript. Writing this article to serve me as a goto article for any interview preparation in future and for others who want to learn. I have compiled all the resources from [MDN Docs](https://developer.mozilla.org/en-US/), [FreeCodeCamp](https://www.freecodecamp.org/learn/javascript-algorithms-and-data-structures/#object-oriented-programming) and [Javascript.info](https://javascript.info).

Lets dive into the discussion without wasting time.

## Primitive and Non-primitive data-types

There are 8 data types in Javascript

- `number` - numbers of any kind
- `bigint` - numbers of large length
- `string` - set of characters wrapped within quotes (" " or ' ') or backticks (\` \`)
- `null` - unknown values
- `undefined` - unassigned values
- `boolean` - true  or false values
- `symbol` - for unique identifiers
- `object` - for complex data structures

Refer [here](https://javascript.info/types) for more information.

Except `object` all others are primitive datatypes. Objects are used to store collections of data in the form of key-value pairs.

## Introduction to Objects

A object is as simple as  `{}`

```JavaScript
typeof {} //"object"
```
### Object creation

Objects can be created in two ways

1. **Object Construtor Syntax**

    ```JavaScript
    let person = new Person()
    ```
2. **Object Literal Syntax**

    ```JavaScript
    let person = {}
    ```

Objects usually have properties and methods. We can add properties into a object in the form `key:value`. Lets add two properties in `person` object

```JavaScript
let person = {
  name: "john",
  age: "24"
}
```
`name` and `age` are the properties and `"john"` and `"24"` are their respective values.

### Accessing properties

#### Using Dot (.) notation

We can access the properties in the format `<objectName>.<propertyName>`

```JavaScript
let person = {
  name: "john",
  age: "24"
}

// Accessing name
console.log(person.name) // "john"
console.log(person.age) // 24
```

#### Using Square ([]) brackets

The square brackets come in handy when we have multi-word key i.e., object key with more than one word and has a space (' ') in between the words. 
```
let person = {
  name: "john",
  age: "24",
  "favourite language": "javascript" // note the special way in which key is defined
}

console.log(person["name"]) // "john"
console.log(person["favourite language"]) // "javascript"

```
If we use the dot (.) notation to access multiword properties, JavaScript will throw an error. Javascript searches for property `favourite` in the object person. and then gives a syntax error when comes across unexpected `language`.

```JS
console.log(person.favorite language)
```

The square bracket notation is also used to evaluate an expression and find a property of that result
```
let key = "favourite language"
console.log(person[key]) // javascript
```
### Adding and Deleting properties to objects 

We can add new properties to object using the dot notation after object has been created. To the same `person` object from above, we can add new property called `email` as below

```JS
person.email = "test@xyz.com"

console.log(person.email) // test@xyz.com
```
You can delete a property by object using `delete` followed by `<objectName>.<propertyName>`

```JS
delete person.age // deletes age property
```
## References and copying

Most questions in the interviews around the object, if you are beginner, will be around this topic

> 
The key difference between and primitive data types and objects is that Objects are stored and copied by reference

Let us understand the above statement with below example

```JS
// consider a variable of primitive type 
let a = 4;

// suppose you want to create a copy of the above variable
let b = a;

console.log(a) // 4
console.log(b) // 4

// at some point later in your code you change the value of a to be something else
a = 5;

console.log(a) // 5
console.log(b) // 4
```
In the case of primitive data types the values are copied as a whole i.e., each variable has a separate location in the memory and contains its own copy of the value. The above example stands true for all primitive data types.

Now lets check similar scenario with Objects

```JS
// consider a object person with name and email properties

let person = {
  name:"john",
  email : "john@xyz.com"
}

// you want to make a copy of this object

let newPerson = person

// you want to change the value of newPerson to be something else

newPerson.name = "doe"

console.log(person.name) // "doe"
console.log(newPerson.name) // "doe"

```
This happens because Objects are stored as references. There is a single object with parameters `name` and `email` in memory and refernced by two varaibles `person` and `newPerson`. We are making change to same object, hence those changes are reflected in both the variables.

### Copying Objects

As we have seen above, normal copy just creates a refernce to the same object. If we want to create a separate clone of the object, we can loop through the entire object using `for...in` loop and copy each property. 

#### Using Object.assign()

The other way we can do it is using `Object.assign()` method. According to [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign), it can be used to copy its **own enumerable properties**. It cannot be used to copy nested objects. The nested object is still copied as a reference.

```JS
let person = {
  name:"john",
  email : "john@xyz.com"
}

let newPerson = {}
Object.assign(newPerson, person)

newPerson.name = "doe"

console.log(person.name) // "john"
console.log(newPerson.name) // "doe"
}
```
#### Using Spread Operator (...)

Spread operator was added in ES6. It is used to spread or expand an iterable such as array or object. It can be used to copy objects. This copies without reference.

```JS
const user = { name: "john", age: "24" }
const newUser = {...user}
console.log(newUser) // { name: "john", age: "24" }

newUser.name = "lewis"
console.log(newUser.name) // "lewis"
console.log(user.name) // "john"
```
#### Deep Copying

Copying using `Object.assign()` and `Spread Operator (...)` does not copy the nested object - object which contains object in it. The nested part of object is still copied as a reference.

We can do a  **Deep Copy** of an object using `JSON.stringify()` and `JSON.parse()`

```JS
const user = {
  name: "john", 
  age: "24", 
  address: {
    state: "Texas",
    country: "USA"
  }
}

const newUser = JSON.parse(JSON.stringify(user))
newUser.address.state="California"

console.log(user) 
// { name: "john", age: "24", address:{ state: "Texas", country: "USA" }}
console.log(newUser) 
// { name: "john", age: "24", address:{ state: "California", country: "USA" }}
```
## Object Methods

Objects along with properties have methods. Lets add a method `sayHello()` to our userobject.

```JavaScript
const user = {
  name:"john",
  age: "24",
  sayHello: function() {
    console.log(`Hello ${user.name}`);
  }
}

user.sayHello() // "Hello john"
```
The idea behind having methods in objects is to allow only the methods to manipulate the object. The above code would work perfectly fine. But if the object is copied to another object and we call the `sayHello()` method, it will cause an error as it will try to access the method from another object.

```JS
let user = {
  name: "John",
  age: 30,
  sayHi() {
    console.log( user.name ); // leads to an error
  }
};

let admin = {...user};
user = null; // overwrite to make things obvious
admin.sayHi();
```
To overcome this, we need to use `this` keyword while refering any property of the object inside its method. `this` inside the method of an object refers to the enclosing object (the object in which the method is present).

The `this` behaves differently in JavaScript. Its value is evaluated at the run time depending upon the context. Please do give this [article](https://javascript.info/object-methods#this-is-not-bound) a read. 
Arrow functions don't have their own `this`. [They derive it from their enclosing Lexical Context](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this#arrow_functions)

## Constructors

So far we have seen how to create objects. What if we need to create multiple objects of the same type for example a list of users, list of todo items, we need to use Constructors along with `new` keyword to create objects. 

Constructors are functions that create new objects - they define both the properties and methods that can act upon these properties. It serves as a blue print to the create objects. 

Constructors are functions with just two conventions
1. First letter of function should be capital
2. Constructors should be called only with `new` keyword

```JS
function User(name, age) {
  this.name = name,
  this.age = age
}
```
The above snippet creates a Constructor with name `User`. Constructors use `this` keyword to set the properties of the new object. Here, `this` refers to the new object being created. The above Constructor defines two properties name and age and sets their values to the parameters in Constructor definition. 

```JS
const user = new User("john", "24")
console.log(user.name) // "john"
```
We can create a new user as above. The `new` operator tells JavaScript to create a new instance of `User` object. Pass the values for name and age as arguments to the constructor call. We can have the methods in the constuctor function in the same way we have the methods in normal objects.

```JS
function User(name, age) {
  this.name = name,
  this.age = age,
  this.sayHi = function() {
    console.log(`Hi ${this.name}`)
  }
}

const user = new User("john", "24")
user.sayHi() // "Hi john"
```
### Instance of

When we create a new object using constructor, the new object is said to be instance of the constructor. This can be verified using instance of operator. It returns true if it is instance of the constructor

```JS
function User(name, age) {
  this.name = name,
  this.age = age,
  this.sayHi = function() {
    console.log(`Hi ${this.name}`)
  }
}

const user = new User("john", "24")
console.log(user instanceof User) // true
```
The `instanceof` operator can be used to distinguish between normal objects and objects created normally.

## Call, apply and bind

The `this` keyword's value is calculated at the runtime. Inside a event listener or when the Object's method is passed as a callback to `setTimeout()`, `this` context is lost. The value of `this` will be the element that recieved the event in event listener function. I 

To overcome this JavaScript provides, with `bind()` method. Along with `bind()` it is good to have idea about `call()` and `apply()` methods.

### call() and apply()

This is a function borrowing method in Javascript. The `call()` method calls a function with a given `this` context and arguments provided induvidually.

```JS
const user1 = {
  fname: "john",
  lname: "doe",
  getFullName: function() {
    console.log(`Hello ${this.lname}, ${this.fname}`) 
  }
}

user1.getFullName() // "Hello doe, john"
```
Consider there exists a object `user2` which does not have the getFullName implementation. We can call getFullName and provide user2 as the context.

```JS
const user2  = { 
  fname: "Alfred", 
  lname: "Marshall" 
}

user1.getFullName.call(user2) // "Hello Marshall, Alfred"
```
It is a good practice to have these functions outside. We can pass additional arguments to the call function induvidually. Apply works the same way as call but it takes the additional arguments in an array

```
const user1 = {
  fname: "John",
  lname: "Doe",
}

const greetNewJoiner = function(teamName, location) {
  console.log(`Hello ${this.lname}, ${this.fname}`) 
  console.log(`We are happy to welcome you to our ${location} office and into ${teamName} team`)
}

const user2  = { 
  fname: "Alfred", 
  lname: "Marshall" 
}

greetNewJoiner.call(user1, "Frontend" , "California")
// "Hello Doe, John"
// "We are happy to welcome you to our California office and into Frontend team"
greetNewJoiner.call(user2, "Devops", "California") 
// "Hello Marshall, Alfred"
// "We are happy to welcome you to our California office and into Devops team"

// Apply example
greetNewJoiner.apply(user2, ["Devops", "California"]) 
// "Hello Marshall, Alfred"
// "We are happy to welcome you to our California office and into Devops team"
```
### bind()

Bind creates a new function and returns the function with newly bound `this` context.

```JS
const user = {
  fName: "Kevin",
  sName: "Edwards",
}

const greetUser =  function() {
  console.log(`Welcome ${this.sName}, ${this.fName}`)
}

const admin = {
  fName: "John",
  sName: "Doe",
}

const greetAdmin = greetUser.bind(admin)

greetAdmin()
// "Welcome Doe, John"
```

Most of the questions in interview will be around these three methods.

That's all for this article. Please let me know if I need to include anything else. 

Thanks for reading.


