maneiras de lidar com arrays, objetos e arrays-like - parte 2

04-10-2018

Na primeira parte desse artigo falamos sobre algumas maneiras de iterar sobre arrays e arrays-like além dos loops tradicionais.

Nessa segunda parte iremos discutir algumas maneiras de iterarmos sobre as propriedades de um objeto.
Em todos os exemplos de códigos no decorrer deste artigo, levarei em consideração o objeto literal criado (desculpe a falta de imaginação para elaborar os exemplos):

1
2
3
4
5
6
7
8
9
10
11
12
const mamifero = {
alimento: 'leite',
locomove: null
}
const gato = {
locomove: '4 patas',
som: 'miau'
}
const cachorro = {
locomove: '4 patas',
som: 'au-au'
}

Agora vou vincular os devidos protótipos

1
2
Object.setPrototypeOf(gato , mamifero )
Object.setPrototypeOf(cachorro , mamifero )

O que eu fiz aqui foi falar que gato e cachorro herdam as propriedades de mamifero através de sua cadeia de protótipos.
Na prática isso quer dizer que gato.alimento irá retornar “leite”. Já gato.locomove irá retornar “4 patas” e não null, pois esta propriedade foi substituída no próprio objeto gato.

Agora que temos nossos objetos de exemplos criados, vamos ao que interessa e iterar sobre suas propriedades.

A maneira mais simples e mais verbosa de olharmos para as propriedades de um objeto é utilizando o loop for … in. Veja no exemplo:

1
2
3
for (let propriedade in gato){
console.log( propriedade )
}

Isso irá mostrar no console a saída:
locomove
som
alimento

Ou seja, serão mostrados no console os nomes das propriedades, e não os seus valores. O exemplo abaixo mostra como listar também os respectivos valores

1
2
3
for (let propriedade in gato){
console.log( propriedade, 'possui valor: ', gato[propriedade] )
}

Compare a saída no console e veja a diferença.

Você pode ter percebido que além das propriedades do objeto gato, o loop for … in mostrou também as propriedades herdadas de mamifero. Se não quisermos que isso aconteça, basta perguntarmos dentro do loop se a propriedade pertence ao próprio objeto, assim:

1
2
3
for (let propriedade in gato){
gato.hasOwnProperty(propriedade) && console.log( propriedade, 'possui valor: ', gato[propriedade] )
}

Esse curto-circuito está aí apenas para mostrar que não precisamos necessariamente de um if. Mas o resultado é o mesmo de:

1
2
3
4
5
for (let propriedade in gato){
if(gato.hasOwnProperty(propriedade)){
console.log( propriedade, 'possui valor: ', gato[propriedade] )
}
}

Um pouco mais verboso mas com o mesmo resultado final.

Se você digitar no console gato.toString() verá que a saída não é lá muito amigável. Que tal se nós sobrescrevermos o método toString() dentro de gato

1
2
3
4
5
6
7
const gato = {
locomove: '4 patas',
som: 'miau',
toString: function(){
return 'eu sou um objeto gato'
}
}

O comando gato.toString() irá mostrar uma mensagem muito mais amigável no console, mas isso nos trouxe um problema, o loop for … in está nos mostrando no console a implementação de toString().
Umas das maneiras de mostrar apenas as propriedades seria efetuar um typeof em cada propriedade e mostrar apenas as que são strings, números, booleanos, null e undefined. Mas veremos um jeito melhor.

Propriedades enumeráveis.

Todas as propriedades que criamos normalmente em um objeto são criadas com uma flag interna chamada Enumerable setada para true. Na prática, isso quer dizer que estas propriedades são listadas num loop for … in, por exemplo.

A boa notícia é que há uma maneira de criar uma propriedade não enumarada. Isso se dá através de um método de Object chamado defineProperty(). Vamos ao código:

1
2
3
4
5
6
7
Object.defineProperty(cachorro, 'toString', {
enumerable: false,
writable: false,
value: function(){
return 'eu sou um objeto gato'
}
})

Agora ao executarmos o mesmo loop no objeto cachorro, veremos um resultado levemente diferente:

1
2
3
4
5
for (let propriedade in cachorro){
if(cachorro.hasOwnProperty(propriedade)){
console.log( propriedade, 'possui valor: ', cachorro[propriedade] )
}
}

Perceba que agora a implementação de toString não é mais mostrada no console.

O método defineProperty recebe três parâmetros: objeto que queremos inserir a propriedade, o nome da propriedade, um objeto descritor. Para saber quais outras configurações podemos utilizar, dê uma olhada na documentação

keys(), values() e entries()

Alguns métodos de Object podem tornar nosso código mais sucinto. O primeiro que veremos é o keys().

Esse método retorna um array contendo as propriedades do próprio objeto (descartando, portanto, a cadeia de protótipos) se esta propriedade for enumerável, obviamente.

1
2
Object.keys(gato) //irá retornar ["locomove", "som", "toString"] 
Object.keys(cachorro) //irá retornar apenas ["locomove", "som"]

Repare que em nenhum dos casos a propriedade alimento foi listada, pois esta não está presente no objeto em si.

Já o método values() retorna os valores, e não as propriedades. Veja:

1
Object.values(cachorro) //irá retornar ["4 patas", "au-au"].

Você deve ter percebido que enquanto o loop for … in itera sobre as propriedades enumeráveis próprias e herdadas, keys() e values() iteram somente sobre as propriedades enumeráveis próprias, não as herdadas. Essa mesma característica acontece com entries(), que veremos a seguir.

Object.entries() retorna um array onde cada elemento é outra array contendo a chave no índice 0 e o valor no índice 1. Veja o exemplo:

1
2
3
4
5
6
Object.entries(cachorro) //irá retornar a seguinte Array:

//[
// ["locomove", "4 patas"],
// ["som", "au-au"]
//]

Uma observação importante é que nem values() nem entries() funcionam nativamente em browsers antigos

Conclusão

Vamos ver agora como podemos tirar vantagem de keys() (melhor suporte em navegadores antigos), para iterar sobre o objeto cachorro e mostrar suas propriedades no console

1
2
3
for (let propriedade of Object.keys(cachorro)){
console.log( propriedade, 'possui valor: ', cachorro[propriedade] )
}

Você pode ter percebido que utilizei um outro tipo de loop, o for … of. Sabemos que Object.keys() devolve uma array e que o uso do for in em arrays é uma má prática. O ES6 introduziu portanto o loop for … of que nos permite iterar sobre o que chamamos de um “iterável”. Mas isso é assunto para um próximo artigo.

Um abraço

Aproveito para fazer o meu jabá e dizer que o meu curso de Javascript Completo tem um desconto bacanão se você adquirir através dessa página .


Comentários: