Operador Spread no Javascript

Publicado em: 10/07/2020

O operador spread (que dependendo de sua utilização pode ser operador spread ou rest, veja mais sobre o operador Rest nesse link: Operador Rest no Javascript) serve para espalhar elementos de um objeto iterável, como uma array por exemplo, para dentro de uma outra estrutura de dados compatível, ou mesmo agrupar propriedades ou parametros de funções em uma única coisa só.

Vamos preparar como de costume um exemplo para poder vizualizar melhor:

// Inventário do jogador
const inventario = [
  'Poção de Mana',
  'Poção de Mana',
  'Espada de Aço +10',
  'Carne de Troll'
]

// Itens adquiridos após derrotar um troll da neve
const iceTrollLoot = [
	'Carne de Troll',
  'Pele de Troll',
  'Orbe de Gelo'
]

Nesse exemplo temos nosso inventário e o loot que adquirimos após derrotar um troll da neve em nosso jogo imaginário. Caso quisessemos somar os itens de loot ao nosso inventário com o operador spread podemos simplesmente fazer o seguinte:

const inventarioAtualizado = [
  ...inventario,
  ...iceTrollLoot
]

console.log(inventarioAtualizado)

Entendendo o Spread

Explicando o que foi feito, para manter os princípios da imutabilidade criamos uma nova array para nosso inventário e então espalhamos os itens do inventário antigo e os itens do loot en nossa nova array. Entenda como itens cara uma string presente na array. Se tentássemos apenas adicionar as arrays dentro da nova array sem o spread teríamos uma array de arrays, e não é o que queremos. Veja:

const inventarioAtualizado = [
  inventario,
  iceTrollLoot
]

console.log(inventarioAtualizado)

// resultado: 
// [
//   [
//     'Poção de Mana',
//     'Poção de Mana',
//     'Espada de Aço +10',
//     'Carne de Troll'
//   ],
//   [ 'Carne de Troll', 'Pele de Troll', 'Orbe de Gelo' ]
// ]

O que precisamos alcançar nessa situação é ter todos os itens na mesma array e no mesmo nível, o que pode ser alcançado se além de simplesmente jogar as arrays dentro de outra nós espalharmos os itens destas numa nova. É como se pegássemos as arrays, removessemos as chaves e então jogássemos dentro da nova array.

Copiando uma array usando o Spread

O operador spread também é bastante útil quando queremos criar uma cópia de um iterável como uma array, por exemplo. Considere o exemplo a seguir:

const heroLog = [
	'(dano) Levou 50 hitpoints de dano',
  '(dano) Levou 50 hitpoints de dano',
  '(ataque) Inferiu 200 hitpoints de dano',
  '(dano) Levou 50 hitpoints de dano'
]

No exemplo temos um log de ações do nosso personagem, que parece estar lutando com algum monstro. Se por algum motivo, em nossa aplicação, precisassemos fazer uma cópia dessa array de logs, como fariamos? Talvez alguém sugira isto:

const heroLogCopy = heroLog;

Mas o exemplo acima não é válido, pois o que está sendo feito é apenas atrelando a variável heroLogCopy uma referência à array original, e não uma cópia de fato. Veja:

// Tentando fazer uma "cópia":
const heroLogCopy = heroLog;

// Tentando modificando a array original, mas não a "cópia":
heroLog.push('Ué, eu não deveria aparecer na cópia!')

// Vamos então logar nossa "cópia" e ver que na verdade ela não é uma cópia,
// mas apenas uma referencia a array original:
console.log(heroLogCopy)

// Resultado:
// [
//   '(dano) Levou 50 hitpoints de dano',
//   '(dano) Levou 50 hitpoints de dano',
//   '(ataque) Inferiu 200 hitpoints de dano',
//   '(dano) Levou 50 hitpoints de dano',
// 
//   // Esse item não deveria aparecer:
//   'Ué, eu não deveria aparecer na cópia!'
// ]

Como você pode ver, no exemplo acima, heroLogCopy apenas é um "atalho" para a array original. Com o spread podemos de fato copiar a array para um novo item:

// Criando uma cópia de fato:
const heroLogCopy = [...heroLog]

// Modificando a array original (heroLog):
heroLog.push('Ué, eu não deveria aparecer na cópia!')

// E de fato, o novo item não apareceu, pois "heroLogCopy"
// é, de fato, uma nova cópia da array "heroLog" feita
// antes dela ser modificada.
console.log(heroLogCopy)

// Resultado:
// [
//   '(dano) Levou 50 hitpoints de dano',
//   '(dano) Levou 50 hitpoints de dano',
//   '(ataque) Inferiu 200 hitpoints de dano',
//   '(dano) Levou 50 hitpoints de dano'
// ]

Copiando objetos usando spread

Ainda, podemos também criar uma cópia rasa de um objeto para dentro de um novo usando essa mesma sintaxe. Considere o exemplo:

// Um objeto padrão com dados reutilizáveis:
const fichaBase = {
  classe: 'Dark Mage',
  race: 'Human',
  gold: 0
}

// Aqui estamos criando meu personagem, copiando o objeto anterior nesse novo:
const gughog = { ...fichaBase }

console.log(gughog)

// Resultado:
// { classe: 'Dark Mage', race: 'Human', gold: 0 }

Melhor ainda, poderiamos, ainda com base no exemplo anterior, não só criar uma cópia do objeto, mas criar um novo objeto com propriedades adicionais:

const gughog = {
  ...fichaBase, // espalhamos as propriedades do objeto
  name: 'Gughog',
  level: 1
}

console.log(gughog)

// Resultado:
// {
//  classe: 'Dark Mage',
//	race: 'Human',
//  gold: 0,
//  name: 'Gughog',
//  level: 1
//}

O que foi feito foi o seguinte: criamos um novo objeto, espalhamos as propriedades do objeto fichaBase dentro dele, desempacotando-as das chaves e espalhando de fato, e declaramos mais propriedades normalmente.

Considerações finais

Toda essa nova sintaxe é extremamente útil, principalmente quando usamos bibliotecas Javascript como React.js ou Vue.js (mais informações sobre estes em posts futuros) que nos encorajam a usar recursos modernos provenientes da ECMAScript 5 em diante e acaba tornando-se uma forma bastante intuitiva e simples de realizar tarefas antes feitas de outras formas. Como dito no inicio do post, dependendo da forma que você usa esses "três pontinhos" ela pode ser Rest ou Spread, e nesse post foi abordado o uso dele como Spread. Leia aqui sobre o uso dele como Rest: Operador Rest no Javascript. Até o próximo post!