Object Manipulation
Tags: webdev, javascript, beginners, programming
Published: 2023-06-13 03:27:33

## What is Object?

An object is a collection of properties, and a property is an association between a `key` and a `value`. A property's value can be a `number`, `string`, `boolean`, `null`, `undefined`, `object`, or `function`. A property's key can be any `string`, including an empty string.

We can create an object, assign it to a variable, add properties to it, and delete properties from it, and we can also access the values of the properties using the dot notation or the bracket notation. Consider the following example:

```js
const obj = {
	name: "Rizwan",
	dobYear: 1996,
	isDeveloper: true,
	hobbies: ["Coding", "Gaming", "Reading"],
	age: function () {
		return 2023 - this.dobYear;
	},
	address: {
		city: "Karachi",
		country: "Pakistan",
	},
};

console.log(obj.name); // Rizwan
console.log(obj.dobYear); // 1996
console.log(obj.isDeveloper); // true
console.log(obj.hobbies); // ["Coding", "Gaming", "Reading"]
console.log(obj.age); // 27
console.log(obj.address); // { city: 'Karachi', country: 'Pakistan' }

delete obj.isDeveloper; // delete property from object

console.log(obj.isDeveloper); // undefined

// add new property to object

obj.stacks = ["JavaScript", "React", "Node"];

// update property value

obj.name = "Rizwan Ashiq";

// Can also use bracket notation to access properties

console.log(obj["name"]); // Rizwan Ashiq
console.log(obj["dobYear"]); // 1996
console.log(obj["hobbies"]); // ["Coding", "Gaming", "Reading"]
```

## Setting properties of `undefined`

Consider the following example:

```js
const obj = {
	name: "Rizwan",
};

// This will error
obj.address.city = "Karachi"; // Cannot set properties of undefined (setting 'city')
```

In the above example, we are trying to access (update) the `city` property of the `address` property of the `obj` object. But the `address` property is not defined in the `obj` object. So, we will get an error. To avoid this error, we can use the `&&` operator, or optional chaining `?` to check if the `address` property is defined or not.

```js
const obj = {
    name: "Rizwan",
}

// And operator
obj.address && (obj.address.city = "Karachi");
// It will not throw an error, but it will simply not move forward after the && operator.

// Optional chaining
obj.address?.city = "Karachi";
// Same as above, if the address property is not defined, it will not go forward.
```

Even if the `address` property is not defined, the above code will not throw an error, but it will not create the `address` property either. To create the `address` property, we can use the ternary operator.

```js
const obj = {
	name: "Rizwan",
};

// Ternary operator
obj.address
	? (obj.address.city = "Karachi")
	: (obj.address = { city: "Karachi" });
```

## `for..in` loop

The `for..in` loop iterates over the enumerable properties of an object, in an arbitrary order. For each distinct property, statements can be executed.

```js
const obj = {
    name: "Rizwan",
    dobYear: 1996,
    isDeveloper: true,
    hobbies: ["Coding", "Gaming", "Reading"],
    age: function () {
        return 2023 - this.dobYear;
    },
    address: {
        city: "Karachi",
        country: "Pakistan",
    },
};


for (let key in obj) {
    console.log(key); // name, dobYear, isDeveloper, hobbies, age, address
    console.log(obj[key]); 
    // Rizwan, 1996, true, ["Coding", "Gaming", "Reading"], 27, { city: 'Karachi', country: 'Pakistan' }
}
```

It's good tool to iterate over the object properties.

## What is enumerable property?

_It's a lot more boring than something that should be visualized_ 😒. And you won't be dealing with this. It's just for information.

An enumerable property is one that can be included in and visited during `for..in` loops (or a similar iteration of properties, like `Object.keys()`). By default, all properties of an object are `enumerable`, unless you set the `enumerable` attribute to `false`.

```js
const person = { age: 18 };
Object.defineProperty(person, "name", { value: "Joshua", enumerable: false });

person.name; // 'Joshua'
for (prop in person) {
	console.log(prop);
} // 'age'
Object.keys(person); // ['age']
Object.getOwnPropertyNames(person); // ['age', 'name'] (non-enumerable properties are included)
```

## Object Methods

In JavaScript, there are many methods that we can use to manipulate objects. Here are some of them:

- `Object.keys()`
- `Object.values()`
- `Object.entries()`
- `Object.fromEntries()`
- `Object.assign()`
- `Object.seal()`
- `Object.freeze()`
- `Object.preventExtensions()`
- `Object.isSealed()`
- `Object.isFrozen()`
- `Object.isExtensible()`
- `Object.is()`

### `Object.keys()`

Returns an array of a given object's own `enumerable property` names, iterated in the same order that a normal loop would.

```js
const obj = {
	name: "Rizwan",
	age: 25,
	city: "Rahim Yar Khan",
};

console.log(Object.keys(obj)); // [ 'name', 'age', 'city' ]
```

By using `Object.keys()` we can get all the keys of an object and then we can use `forEach` loop to iterate over the keys and get the values of the object.

```js
const obj = {
	name: "Rizwan",
	age: 25,
	city: "Rahim Yar Khan",
};

Object.keys(obj).forEach((key) => {
	console.log(obj[key]); // Rizwan, 25, Rahim Yar Khan
});
```

By using `Object.keys()` we can also get the `length` of an object.

```js
const obj = {
	name: "Rizwan",
	age: 25,
	city: "Rahim Yar Khan",
};

console.log(Object.keys(obj).length); // 3
```

You can also use `Object.keys()` to check if an object is `empty` or not.

```js
const obj = {
	name: "Rizwan",
	age: 25,
	city: "Rahim Yar Khan",
};

if (Object.keys(obj).length === 0) {
	console.log("Object is empty");
} else {
	console.log("Object is not empty");
}
```

I would recommend you to play with `Object.keys()` and you will find many more use cases of it 😊

### `Object.values()`

Returns an array of a given object's own enumerable property values, in the same order as that provided by a `for...in` loop (the difference being that a `for-in` loop enumerates properties in the prototype chain as well).

```js
const obj = {
	name: "Rizwan",
	age: 25,
	city: "Rahim Yar Khan",
};

console.log(Object.values(obj)); // [ 'Rizwan', 25, 'Rahim Yar Khan' ]
```

### `Object.entries()`

Returns an array of a given object's own enumerable string-keyed property `[key, value]` pairs, in the same order as that provided by a `for...in` loop. The order of the array returned by `Object.entries()` does not depend on how an object is defined. If there is a need for certain ordering, then the array should be sorted first like `Object.entries(obj).sort((a, b) => b[0].localeCompare(a[0]));`.

```js
const obj = {
	name: "Rizwan",
	age: 25,
	city: "Rahim Yar Khan",
};

console.log(Object.entries(obj)); // [ [ 'name', 'Rizwan' ], [ 'age', 25 ], [ 'city', 'Rahim Yar Khan' ] ]
```

### `Object.fromEntries()`

The `Object.fromEntries()` method transforms a list of key-value pairs into an object.

```js
const arr = [
	["name", "Rizwan"],
	["age", 25],
	["city", "Rahim Yar Khan"],
];

console.log(Object.fromEntries(arr)); // { name: 'Rizwan', age: 25, city: 'Rahim Yar Khan' }
```

### `Object.assign()`

The `Object.assign()` method copies all enumerable own properties from one or more source objects to a target object. It returns the modified target object.

```js
const obj1 = {
	name: "Rizwan",
	age: 25,
	city: "Rahim Yar Khan",
};

const obj2 = {
	country: "Pakistan",
};

const obj3 = Object.assign(obj1, obj2);

console.log(obj3); // { name: 'Rizwan', age: 25, city: 'Rahim Yar Khan', country: 'Pakistan' }
```

Shallow copy can also do the same thing as `Object.assign()`.

```js
const obj1 = {
	name: "Rizwan",
	age: 25,
	city: "Rahim Yar Khan",
};

const obj2 = {
	country: "Pakistan",
};

const obj3 = { ...obj1, ...obj2 };

console.log(obj3); // { name: 'Rizwan', age: 25, city: 'Rahim Yar Khan', country: 'Pakistan' }
```

`Object.assign()` is better cause in shallow copy the problem is, if we have a nested object in the source object, then it will only copy the reference of that nested object. So if we change the nested object in the source object, then it will also change in the target object.

```js
const obj1 = {
	name: "Rizwan",
	age: 25,
	city: "Rahim Yar Khan",
	address: {
		street: "Main Road",
		house: 123,
	},
};

const obj2 = {
	country: "Pakistan",
};

const obj3 = { ...obj1, ...obj2 }; // shallow copy

obj3.address.house = 456; // change house number in obj3, it will also change in obj1

console.log(obj1); // { name: 'Rizwan', age: 25, city: 'Rahim Yar Khan', address: { street: 'Main Road', house: 456 } }
console.log(obj3); // { name: 'Rizwan', age: 25, city: 'Rahim Yar Khan', address: { street: 'Main Road', house: 456 }, country: 'Pakistan' }
```

:::

### `Object.seal()`

The `Object.seal()` method seals an object, preventing new properties from being added to it and marking all existing properties as non-configurable. Values of present properties can still be changed as long as they are writable.

```js
const obj = {
	name: "Rizwan",
	age: 25,
	city: "Rahim Yar Khan",
};

Object.seal(obj);

obj.country = "Pakistan"; // It will not add country property to obj, and it will not throw any error
obj.name = "Rizwan Khan"; // It will change the value of name property
delete obj.city; // It will not delete city property from obj, and it will not throw any error

console.log(obj); // { name: 'Rizwan', age: 25, city: 'Rahim Yar Khan' }
```

### `Object.freeze()`

The `Object.freeze()` method freezes an object: that is, prevents new properties from being added to it; prevents existing properties from being removed; and prevents existing properties, or their enumerability, configurability, or writability, from being changed. In essence the object is made effectively immutable. The method returns the object being frozen.

```js
const obj = {
	name: "Rizwan",
	age: 25,
	city: "Rahim Yar Khan",
};

Object.freeze(obj);

obj.country = "Pakistan"; // It will not add country property to obj, and it will not throw any error
obj.name = "Ali"; // It will not change the name property, and it will not throw any error
delete obj.city; // It will not delete the city property, and it will not throw any error

console.log(obj); // { name: 'Rizwan', age: 25, city: 'Rahim Yar Khan' }
```

### `Object.preventExtensions()`

The `Object.preventExtensions()` method prevents new properties from ever being added to an object (i.e. prevents future extensions to the object).

```js
const obj = {
	name: "Rizwan",
	age: 25,
	city: "Rahim Yar Khan",
};

Object.preventExtensions(obj);

obj.country = "Pakistan"; // It will not add country property to obj, and it will not throw any error
obj.name = "Ali"; // It will change the name property
delete obj.city; // It will delete the city property

console.log(obj); // { name: 'Ali', age: 25 }
```

### `Object.isSealed()`

The `Object.isSealed()` method determines if an object is sealed.

```js
const obj = {
	name: "Rizwan",
	age: 25,
	city: "Rahim Yar Khan",
};

Object.seal(obj);

console.log(Object.isSealed(obj)); // true
```

### `Object.isFrozen()`

The `Object.isFrozen()` method determines if an object is frozen.

```js
const obj = {
	name: "Rizwan",
	age: 25,
	city: "Rahim Yar Khan",
};

Object.freeze(obj);

console.log(Object.isFrozen(obj)); // true
```

### `Object.isExtensible()`

The `Object.isExtensible()` method determines if an object is extensible (whether it can have new properties added to it).

```js
const obj = {
	name: "Rizwan",
	age: 25,
	city: "Rahim Yar Khan",
};

Object.preventExtensions(obj);

console.log(Object.isExtensible(obj)); // false
```

## `Object.seal()` VS `Object.freeze()` VS `Object.preventExtensions()`

Difference between `Object.seal()`, `Object.freeze()` and `Object.preventExtensions()` is that `Object.seal()` and `Object.freeze()` make the object non-extensible, but `Object.preventExtensions()` doesn't make the object non-configurable and non-writable.

![Image](https://i.stack.imgur.com/6tIqX.png)

### `Object.is()`

The Object.is() method determines whether two values are the same value.

```js
console.log(Object.is(1, 1)); // true
console.log(Object.is(1, "1")); // false
console.log(Object.is(NaN, NaN)); // true
```

## Operators

### `in` operator

The `in` operator is used to check if an object has a specific property. It returns `true` if the object has the property, and it returns `false` if the object does not have the property.

Here is an example of how to use the `in` operator:

```js
const person = { name: "Ali", age: 30 };

console.log("name" in person); // Output: true
console.log("age" in person); // Output: true

console.log("firstName" in person); // Output: false
console.log("lastName" in person); // Output: false
```

## Summary

In this chapter, we learned about objects in JavaScript. We learned about the different ways to create objects in JavaScript. We also learned about the different ways to access the properties of an object. We learned about the different ways to add, update, and delete the properties of an object. We learned about the different ways to iterate over the properties of an object. We learned about the different ways to copy an object. We learned about the different methods to seal, freeze, and prevent extensions of an object. We learned about the Object.is() method. We learned about the different ways to check if an object is sealed, frozen, or extensible. We learned about the different ways to check if two values are the same value.

| Method                       | Description                                                                                                                                                                                                                                                    |
| ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Object.keys()`              | Returns an array of a given object's own enumerable property names, iterated in the same order that a normal loop would.                                                                                                                                       |
| `Object.values()`            | Returns an array of a given object's own enumerable property values, in the same order as that provided by a `for...in` loop.                                                                                                                                  |
| `Object.entries()`           | Returns an array of a given object's own enumerable string-keyed property `[key, value]` pairs, in the same order as that provided by a `for...in` loop.                                                                                                       |
| `Object.assign()`            | Copies all enumerable own properties from one or more source objects to a target object. It returns the target object.                                                                                                                                         |
| `Object.seal()`              | Prevents new properties from being added to an object and marks all existing properties as non-configurable.                                                                                                                                                   |
| `Object.freeze()`            | Prevents new properties from being added to an object, existing properties from being removed, prevents changing the enumerability, configurability, or writability of existing properties, and prevents the values of existing properties from being changed. |
| `Object.preventExtensions()` | Prevents new properties from ever being added to an object (i.e. prevents future extensions to the object).                                                                                                                                                    |
| `Object.isSealed()`          | Determines if an object is sealed.                                                                                                                                                                                                                             |
| `Object.isFrozen()`          | Determines if an object is frozen.                                                                                                                                                                                                                             |
| `Object.isExtensible()`      | Determines if an object is extensible (whether it can have new properties added to it).                                                                                                                                                                        |
| `Object.is()`                | Determines whether two values are the same value.                                                                                                                                                                                                              |