Desestruturação de objetos e arrays no Javascript (destructuring)

Publicado em: 02/06/2020

A atribuição via desestruturação é uma prática muito comum no desenvolvimento de aplicações modernas com Javascript. Foi introduzido na especificação Ecmascript 6 e trouxe bastante flexibilidade e legibilidade na utilização de arrays e objetos no Javascript.

Consiste basicamente em extrair elementos de dentro de arrays e objetos, de modo a garantir bastante legibilidade e evitar o uso extremo de notação de pontos muito profundas. Veja como geralmente era feito, considerando o objeto abaixo:

var ficha = {
  nome: 'gughog',
  level: 10,
  emblemas: ['Fogo', 'Gelo'],
  itens: {
    cabeca: ['Helmo de aço'],
    torso: ['Manoplas de aço'],
    bracos: ['Calças de aço'],
    pernas: ['Botas de aço', 'Botas de couro +2', 'Botas reluzentes'],
  },
  skills: {
    magia: {
      fogo: 'Bola de fogo +9',
      gelo: 'Tempestade gélida +7',
      eletrico: null,
      terra: null
    }
  }
}

Temos aqui um objeto com diversos níveis de dados. Caso precisemos logar o valor do level da ficha, fazemos algo assim:

console.log(ficha.level)

Caso precisemos acessar um valor mais profundo, como o valor da skill de fogo, fariamos assim:

console.log(ficha.skills.magia.fogo)

Em pequenos objetos/arrays talvez não tenham muitos problemas usar dessa notação de ponto para acessar, caso esses elementos não possuam subelementos muto profundos, mas isso acaba tornando o desenvolvimento cansativo,* verboso, *chato e repetitivo. Isso é tudo que nós desenvolvedores devemos evitar, e é com esses preceitos que o destructuring chegou.

Desestruturando poderiamos simplesmente acessar esses mesmos elementos dessa forma:

console.log(level)
console.log(fogo)

Obviamente poderiamos fazer isso para alcançar o mesmo efeito:

var level = ficha.level
var fogo = ficha.skills.magia.fogo

Mas aí estariamos atribuindo a uma nova variável um valor que já existia, e isso não chega nem perto da elegância e da engenhosidade do destructuring.

Desestruturando objetos

Ainda utilizando-se do nosso objeto ficha, vamos apenas atualizá-lo para o ECMAScript 6, removendo essa horrenda var para usar algo mais moderno:

const ficha = {
  nome: 'gughog',
  level: 10,
  emblemas: ['Fogo', 'Gelo'],
  itens: {
    cabeca: ['Helmo de aço'],
    torso: ['Manoplas de aço'],
    bracos: ['Calças de aço'],
    pernas: ['Botas de aço', 'Botas de couro +2', 'Botas reluzentes'],
  },
  skills: {
    magia: {
      fogo: 'Bola de fogo +9',
      gelo: 'Tempestade gélida +7',
      eletrico: null,
      terra: null
    }
  }
}

Então, via desestruturação, vamos acessar as mesmas variáveis:

const { level } = ficha
console.log(level) // 10

Mas o que diabos aconteceu aqui?!

Utilizando-se das chaves, declaramos nela as variáveis que queremos extrair de um objeto que foi apontado depois do sinal de igual. No geral, podemos ler essa expressão assim:

"Extraio o parametro level do objeto ficha. "

Desestruturando parametros objetos com um nome novo

Caso quisermos extrair level e dar um nome diferente, podemos fazer assim:

const { level: nivelDoPersonagem } = ficha
console.log(nivelDoPersonagem)

Desestruturando parametros de objetos em níveis profundos

No exemplo temos o valor da magia de fogo. Como podemos fazer para acessar esse parametro, já que o mesmo possui mais de um nível de profundidade? Temos duas formas:

  1. Maneira preguiçosa:

    // extraimos o parametro do caminho que fizemos com a notaçãode pontos
    const { fogo } = ficha.skills.magia
    console.log(fogo)
  2. Maneira mais criativa:

    // acessamos nível por nível de um objeto especificado
    const { skills: { magia: {fogo} } } = ficha
    console.log(fogo)

Desestruturando arrays

Também podemos desestruturar arrays e extrair apenas os elementos dos indexes que desejarmos, de modo a evitar acessar elementos via index. Veja como fariamos da maneira antiga:

console.log(ficha.emblemas[0])

No exemplo, acessamos o primeiro item da array de emblemas do nosso personagem.

O destructuring nas arrays funciona de forma muito parecida, com algumas pequenas peculiaridades. Vamos agora desestruturar para acessar o mesmo elemento, via destructuring:

const [ emblemaFogo ] = ficha.emblemas
console.log(emblemaFogo) // Fogo

Para acessar um outro elemento que não o primeiro, podemos simplemente omitir e ir passando adicionando vírgulas:

// aqui omitimos o primeiro item e acessamos o segundo:
const [ , emblemaGelo ] = ficha.emblemas
console.log(emblemaGelo) // Gelo

O nome que usamos dentro da array desestruturada pode ser qualquer um que você queira, funciona como uma variável, então, use sua criatividade.

Desestruturação de elementos profundos de arrays

Para desestruturar arrays contidas em arrays, podemos fazer o seguinte, mas primeiro considere essa array:

const chefes = [
  [ 'Dragão Menor', 'Ciclope Verde', 'Largato Selvagem' ],
  [ 'Reaper', 'Cavaleiro Sombrio', 'Minotauro Vermelho' ]
]

Para desestruturar apenas o primeiro valor da primeira array, fazemos algo assim:

const [[dragaoMenor]] = chefes;
console.log(dragaoMenor)

Se tivessemos níveis ainda mais profundos:

const chefes = [
  [ [['Aranha', 'Besouro']], 'Dragão Menor', 'Ciclope Verde', 'Largato Selvagem' ],
  [ 'Reaper', 'Cavaleiro Sombrio', 'Minotauro Vermelho' ]
]

const [[[[aranha]]]] = chefes;
console.log(aranha) // Aranha

Desestruturação de parametros de funções

É muito comum passarmos parametros para nossas funções e agruparmos todos estes em um objeto só. Considere a função a seguir:

function atacarInimigo (props) {
  // se nenhum inimigo for especificado, a função termina aqui 
  if (props.inimigo === null) {
    console.log('Você tentou atacar alguém... mas não tinha ninguém!')
    return
  }

  // usando template strings
  console.log(
    `Você atacou ${props.inimigo} e proferiu ${props.dano} pontos de dano! `
  )
}

// os parametros da função
const parametros = { inimigo: 'Aranha', dano: 32 }

atacarInimigo(parametros) // Você atacou Aranha e proferiu 32 pontos de dano!

Perceba que acabamos tendo que acessar os parametros via notação de ponto por diversas vezes. Podemos deixar muito mais limpo fazendo o destructuring dos parametros da função. Veja a funçaõ refatorada:

function atacarInimigo (props) {
  const { inimigo, dano } = props
  if (inimigo === null) {
    console.log('Você tentou atacar alguém... mas não tinha ninguém!')
    return
  }

  // usando template strings
  console.log(
    `Você atacou ${inimigo} e proferiu ${dano} pontos de dano! `
  )
}

const parametros = { inimigo: 'Aranha', dano: 32 }

atacarInimigo(parametros)

Leia um pouco sobre template string no nosso post: Concatenando Strings no Javascript

Perceba que agora temos muito menos níveis acessados com a notação de pontos, temos apenas as variáveis em si referenciadas.

Considerações finais

O destructuring é um assunto bastante rico, definitivamente é um tema essencial de se dominar para quem desenvolve aplicações modernas com Javascript, principalmente utilizando React. Espero que o assunto esteja um pouco mais claro agora e até o próximo post!