Capa do post

Sintaxe de Espalhamento, o que é e como usar?

5/12/2023

Se você tá aprendendo Javascript e não está aprendendo já com "as novidades" do ES6 — também conhecido como ES2015 ou EcmaScript6 —, já deve ter se deparado com um cenário onde precisava copiar os itens de um Array ou um Object, e viu como pode ser um pouco trabalhoso fazer esse clone que em versões anteriores faríamos algo assim:

var oldArray = [1, 2, 3, 4]
var newArray = []
    
for (var i = 0; i < oldArray.length; i++) {
  newArray[i] = oldArray[i]
}
    
console.log(newArray) // [ 1, 2, 3, 4 ]

Mas graças aos poderes do ES6, isso pode ser bastante simples.

No ES6, dentre várias novidades que facilitaram nossas vidas, tivemos a introdução da Spread Syntax, que também é conhecida por Spread Operator.

Como o nome já diz, ela é exatamente uma "sintaxe de espalhamento", isso quer dizer que podemos apenas "espalhar" um objeto em outro.

Vamos ver o exemplo anterior, agora com essa sintaxe:

const oldArray = [1, 2, 3, 4]
const newArray = [...oldArray]
    
console.log(newArray) // [ 1, 2, 3, 4 ]

Viu como é bem mais simples? Basta a gente criar um novo objeto, colocar a reticências e em seguida o objeto que queremos espalhar.

Talvez esteja se perguntando se isso apenas faz uma clonagem superficial ou profunda, e sim, é uma clonagem profunda. Isso quer dizer que, apesar de termos dois arrays iguais, se alterarmos um, o outro permanece igual. Vamos ver?

const oldArray = [1, 2, 3, 4]
const newArray = [...oldArray]
    
console.log(oldArray) // [ 1, 2, 3, 4 ]
console.log(newArray) // [ 1, 2, 3, 4 ]
    
oldArray.pop()
    
console.log(oldArray) // [ 1, 2, 3 ]
console.log(newArray) // [ 1, 2, 3, 4 ]
    
newArray.push(5)
    
console.log(oldArray) // [ 1, 2, 3 ]
console.log(newArray) // [ 1, 2, 3, 4, 5 ]

Bastante útil hein.

Como veremos em seguida, isso também funciona com objetos

const oldObj = { lang: 'js', year: 2023 }
const newObj = { ...oldObj }
    
console.log(oldObj) // { lang: 'js', year: 2023 }
console.log(newObj) // { lang: 'js', year: 2023 }
    
delete oldObj.year
    
console.log(oldObj) // { lang: 'js' }
console.log(newObj) // { lang: 'js', year: 2023 }
    
newObj['bestLang'] = 'py'
    
console.log(oldObj) // { lang: 'js' }
console.log(newObj) // { bestLang: 'py', lang: 'js', year: 2023 }

Apesar de ser excelente e facilitar muito, você deve ter em mente que com objetos aninhados pode não funcionar como talvez espera.

Vejamos este caso

const oldObj = {
  userId: 0,
  bestFriend: {
    userId: 1
  }
}
    
const newObj = {
  userId: 0,
  bestFriend: {
    bestFriend: {
      userId: 2
    }
  }
}
    
console.log({
  ...newObj,
  ...oldObj
})

Usando isto talvez você esperasse uma saída, assim:

{
  userId: 0,
  bestFriend: {
    userId: 1,
    bestFriend: {
      userId: 2
    }
  }
}

Mas ao invés disso, teremos algo assim:

{
  userId: 0,
  bestFriend": {
    userId: 1
  }
}

Talvez um pouco decepcionante, não?

Isso se deve ao fato de a clonagem ser de elementos simples e, se o objeto já tiver elementos com a mesma key que o atual elemento sendo clonado, o novo apenas sobrescreve o último. Isso é, ele não clona com profundidade, somente joga as key/value lá sem se aprofundar.

Para fazermos isso, precisaríamos criar algum código recursivo de clone/merge, ou apenas conhecermos as keys cujos valores sejam objetos, como algo assim:

{
  ...newObj,
  ...oldObj,
  bestFriend: {
    ...newObj.bestFriend,
    ...oldObj.bestFriend
  }
}

Dessa forma, agora temos um objeto como queríamos.

Concordemos, esse operador é bastante útil, além de facilitar nossas vidas as vezes.

Assim chegamos ao fim. Espero que tenhamos aprendido algo novo hoje.

Onde me encontrar