Programação funcional e a imutabilidade: 4 maneiras de clonar objetos no JavaScript
Tags: javascript, programming, webdev, computerscience
Published: 2023-04-10 12:33:02

A imutabilidade é um princípio da Programação Funcional que se baseia em não alterar o estado dos dados.

No entanto, no JavaScript, as variáveis não-primitivas, tais como objetos e arrays, podem ser modificadas, mesmo quando declaradas utilizando a palavra reservada `const`.

E, para aplicar a imutabilidade usando a linguagem de programação JavaScript, vamos aprender 4 maneiras de clonar objetos ao invés de modificá-los.

## Operador Spread

Com este operador podemos copiar o conteúdo de um objeto ou array para uma nova variável:

```js
const person1 = {
    name: "Anne",
    role: {
        name: "Software Engineer"
    }
}

const person2 = { ...person1 }
person2.name = "John"

console.log(person1)
// { name: "Anne", role: { name: "Software Engineer" }}

console.log(person2)
// { name: "John", role: { name: "Software Engineer" }}
```

Mas este operador usa uma técnica chamada **Shallow cloning** que basicamente clona apenas a raiz do seu objeto para poupar processamento, e para todos os subs objetos é usado a referência do objeto principal.

Isso significa que, ao alterar a propriedade `role.name` do objeto `person2` também vai refletir no objeto `person1`:

```js
person2.role.name = "Associate Software Engineer"

console.log(person1)
// { name: "Anne", role: { name: "Associate Software Engineer" }}

console.log(person2)
// { name: "John", role: { name: "Associate Software Engineer" }}
```

Para resolver isso podemos usar uma técnica chamada **Deep cloning** que consiste em clonar tanto o objeto raiz quanto os subs:

```js
const person1 = {
    name: "Anne",
    role: {
        name: "Software Engineer"
    }
}

const person2 = {
    ...person1,
    name: "John",
    role: { ...person1.role }
}

person2.role.name = "Associate Software Engineer"

console.log(person2)
// { name: "John", role: { name: "Associate Software Engineer" }}

console.log(person1)
// { name: "Anne", role: { name: "Software Engineer" }}
```

## Object.assign

Com este método podemos copiar ou mesclar o conteúdo de um ou mais objetos para uma nova variável: 

```js
const person1 = {
    name: "Anne",
    role: {
        name: "Software Engineer"
    }
}

const person2 = Object.assign({}, person1)
person2.name = "John"

console.log(person1)
// { name: "Anne", role: { name: "Software Engineer" }}

console.log(person2)
// { name: "John", role: { name: "Software Engineer" }}
```

Mas este método também usa a técnica **Shallow cloning**. Portanto, é recomendado que este método seja utilizado para clonar objetos que não tenham objetos aninhados.

## Object.freeze

Já este método, além de copiar o conteúdo de um objeto para uma nova variável, também deixa as propriedades congeladas, ou seja, as propriedades não podem ser modificadas:

```js
const person1 = {
    name: "Anne",
    role: {
        name: "Software Engineer"
    }
}

const person2 = Object.freeze(person1)
person2.name = "John"

console.log(person1)
// { name: "Anne", role: { name: "Software Engineer" }}

console.log(person2)
// { name: "Anne", role: { name: "Software Engineer" }}
```

Mas este método também usa a técnica **Shallow cloning**. Portanto, mais uma vez é recomendado que este método seja utilizado apenas para clonar objetos que não tenham objetos aninhados.

## JSON.stringify e JSON.parse

O método `JSON.stringify()` transforma um objeto JSON em uma string:

```js
const person1 = {
    name: "Anne",
    role: {
        name: "Software Engineer"
    }
}

const person2Str = JSON.stringify(person1)
// '{"name":"Anne","role":{"name":"Software Engineer"}}'
```

Já o método `JSON.parse()` transforma uma string JSON válida em um objeto e/ou array:

```js
const person1 = {
    name: "Anne",
    role: {
        name: "Software Engineer"
    }
}

const person2Str = JSON.stringify(person1)
// '{"name":"Anne","role":{"name":"Software Engineer"}}'

const person2 = JSON.parse(person2Str)
person2.name = "John"
person2.role.name = "Associate Software Engineer"

console.log(person1)
// { name: "Anne", role: { name: "Software Engineer" }}

console.log(person2)
// { name: "John", role: { name: "Associate Software Engineer" }}
```

Quando o método `JSON.stringify()` é acionado, ele transforma o conteúdo do objeto original em uma string JSON e as referências dos subs objetos são perdidas.

E ao usar o método `JSON.parse()` todo o objeto e/ou array é recriado em memória.

Portanto, é possível clonar objetos e/ou arrays usando os métodos `stringify` e `parse`. Mas cuidado com seu uso pois pode ocasionar problemas de desempenho.

---

Conhece mais maneiras de clonar objetos no JavaScript? Deixa nos comentários 👇