JavaScript Objects Recap
Tags: webdev, javascript, beginners, programming
Published: 2023-11-04 04:57:38

Before understanding objects let us now discuss about the primitive and non primitive values: 

The primitive values are those which are immutable. 
A **primitive value** is a value that has no properties or methods.

**3.14** is a primitive value 
what does this means?
if x = 3.14, you can change the value of x, but you cannot change the value of 3.14.

A **primitive data type** is data that has a primitive value.

JavaScript defines 7 types of primitive data types:
- `string`
- `number`
- `boolean`
- `null`
- `undefined`
- `symbol`
- `bigint`

example:
```js
let str = 'Hello';
str += ' World'; // Create a new string, 'Hello World', and assign it to str

```
What are JS Objects then?

A JavaScript object is a collection of **named values**
Actually they are also variables, but it can hold many pairs of keys and values:

The objects are mutable, means once assigned the values can be changed.

let us set and example of how to declare an object
```js
let person = {firstName:"John", lastName:"Doe", age:50, eyeColor:"blue"};
```

and these are some of the ways in which we can access the properties of the objects

```js
let person = {firstName:"John", lastName:"Doe", age:50, eyeColor:"blue"};

console.log(person.age) //30
console.log(person["firstName"]) //John
```

This mutability is one of the key characteristics of objects in JavaScript. Let's explore how objects are mutable and what that means in practice:

#### Changing Object Properties:
You can modify the properties of an object by assigning new values to them. This includes adding new properties, updating existing ones, or deleting properties.
```js
// Creating an object
const person = {
  firstName: 'John',
  lastName: 'Doe'
};

// Modifying properties
person.firstName = 'Jane'; // Update existing property
person.age = 30; // Add a new property
delete person.lastName; // Delete a property
```

#### Reference-Based Behavior:
Objects in JavaScript are reference-based data structures. When you assign an object to a variable or pass it as an argument to a function, you are actually working with a reference to the object, not a copy of it. This means that if you modify the object through one reference, those changes will be visible through all references to that object.
```js
const obj1 = { prop: 'value' };
const obj2 = obj1; // Both obj1 and obj2 reference the same object

obj2.prop = 'new value'; // Modifying the object through obj2
console.log(obj1.prop); // Outputs 'new value' because obj1 and obj2 reference the same object
```

### Passing by Reference:
When you pass an object as an argument to a function, you are passing a reference to that object. Any changes made to the object inside the function will affect the original object.

```js
function updateObject(obj) {
  obj.prop = 'updated value';
}

const myObject = { prop: 'original value' };
updateObject(myObject);
console.log(myObject.prop); // Outputs 'updated value' because the object was modified inside the function
```


Let us consider another example

```js
const person = {
  fname : 'John',
  lname : 'Doe',
  age:30,
  fullName : function(){
    console.log(this.fname + ' ' + this.lname)
  },
  address : {
  street : '123 street',
  city : 'New York',
  state : 'NY'
  }
}

const human = {...person} // the spread operator creates the shallow copy, what does this means ?
human.address.city = 'Los Angeles'
human.age = 31
console.log(person.address.city) //Los Angeles
console.log(person.age) // 30
//so here we can see that the nested object has the same reference to its original one, so
//as the value changes in the copied object it is reflected in the original one as well.

//Also if we assign this person object to another variable, the complete object is copied
//with the reference value

const man = person
console.log(man.fname) //john
man.age = 32
console.log(person.age) // 32 ---notice here it is changed
console.log(human.age) // 31 ---but her id doesnt changed! isnt this is intresting?
```

If you need a deep copy (i.e., a copy where nested objects and arrays are also cloned), you would need to use a recursive function or a library like `lodash` to perform a deep copy manually. The spread operator is convenient for shallow copies and quickly creating new objects or arrays with similar structures, but it doesn't handle deep copies.

You can also check the prototype property as follows, more on [Prototypes](https://dev.to/tanmaycode/prototypes-in-javascript-hik)
```js
console.log(Object.getPrototypeOf(person))
```
#### Deep copy using the lodash library

This is a simplest way to create the deep copy of the nested objects :
```js
const _ = require('lodash');

const originalObj = {
  a: 1,
  b: {
    c: 2,
    d: [3, 4],
  },
};

const deepCopiedObj = _.cloneDeep(originalObj);

// Modify the copied object without affecting the original:
deepCopiedObj.b.c = 5;

console.log(originalObj.b.c); // Outputs 2 (original is unaffected)
console.log(deepCopiedObj.b.c); // Outputs 5 (modified in the copy)

```


### Defining methods inside objects

In JavaScript, there are several ways to define functions as methods within an object. Methods are functions that are attached to objects and can be called in the context of those objects. Here are some common ways to define functions as object methods:

**Method Using Function Expression:**
```js
const myObject = {
  myMethod: function () {
    // Method logic
  }
};

```
**Method Using Method Shorthand (ES6):**
```js
const myObject = {
  myMethod() {
    // Method logic
  }
};

```
**Method as Arrow Function (ES6):**
```js
const myObject = {
  myMethod: () => {
    // Method logic
  }
};

```
Be aware that arrow functions do not have their own `this` context; they capture the `this` value from the surrounding lexical context. See, [This Keyword](https://dev.to/tanmaycode/this-keyword-javascript-simplified-4b3l)

**Method Assignment:**
```js
const myObject = {
  myMethod: someFunction
};

function someFunction() {
  // Method logic
}
```

**Computed Property Method Names (ES6):**
```js
const methodName = 'myMethod';
const myObject = {
  [methodName]() {
    // Method logic
  }
};
```


### What is  `Object.assign` ?

`Object.assign()` is a method in JavaScript that is used to copy the values of all enumerable own properties from one or more source objects to a target object. It is often employed for object cloning or to merge objects. It does not create a new object but instead modifies an existing one.

Here's an example of how `Object.assign()` works:

```javascript
const target = { a: 1, b: 2 };
const source = { b: 4, c: 5 };

const result = Object.assign(target, source);
console.log(target); // { a: 1, b: 4, c: 5 }
console.log(result); // { a: 1, b: 4, c: 5 }
```

In this example, the `Object.assign()` method merges the properties of the `source` object into the `target` object. If the `target` object already has a property that is also present in the `source` object, the value from the `source` object overwrites the value in the `target` object.

The main difference between using `Object.assign()` and the usual object declaration is that `Object.assign()` allows you to merge multiple source objects into a single target object, thereby combining their properties. The usual object declaration, on the other hand, simply creates a new object with the specified properties.

For instance, in the case of the usual object declaration:

```javascript
const obj = { a: 1, b: 2 };
```

the `obj` object is created with the specified properties `a` and `b`. However, you cannot directly merge properties from another object into it without manually copying each property. `Object.assign()` simplifies this process by allowing you to merge properties from multiple source objects into a target object in a single step.

### Creating Objects with constructor v/s usual Object creation

In JavaScript, you can create objects using either the object constructor syntax or the normal object creation syntax. While both methods achieve the same result of creating objects, they differ in terms of syntax and behavior.

1.**Object Constructor Syntax:**
   ```javascript
   const obj = new Object();
   obj.property = "value";
   ```
2.**Normal Object Creation:**
   ```javascript
   const obj = { property: "value" };
   ```

The key differences between using the object constructor syntax and normal object creation are as follows:

- **Syntax Clarity and Conciseness:** The normal object creation syntax is more concise and easier to read, write, and understand. It allows you to create objects in a straightforward and simple manner.
- **Performance:** Normal object creation is generally more performant than using the object constructor syntax, especially when creating multiple objects, as it does not involve the overhead of calling the constructor function.
- **Prototype Chain:** When using the object constructor syntax, the created object inherits properties and methods from the `Object` prototype, while with normal object creation, the prototype chain is not involved.
- **Extensibility:** Both approaches allow you to add properties and methods to the object dynamically. However, the normal object creation syntax is more commonly used in modern JavaScript development for its simplicity and readability.

In practice, the normal object creation syntax is widely preferred due to its simplicity, conciseness, and performance benefits. It has become the standard way to create objects in modern JavaScript applications.