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.