Objeto: Objetos JavaScript, formato JSON
Principais termos técnicos abordados: JSON, propriedade, html, JavaScript, arrow function
Requisitos para as instruções funcionarem: editor de texto e navegador instalados
Requisitos para compreensão das instruções: noções bem básicas de HTML e ter implementado receita anterior
Como ler essa receita: instruções, dentro dos passos da receita e que requerem ação sua no computador, estarão escritas em cor azul. Comentários sobre os passos estarão em fonte normal, cor preta. Comandos, código-fonte, termos técnicos ou configuração explícita, estarão com fonte diferenciada.
Vamos aprender a definir e acessar objetos JavaScript que seguem a sintaxe JavaScript Object Notation - abreviadamente, JSON - lêia jêizon.
Precisamos, para isso, precisamos entender as regras de criação desses objetos, as regras sintáticas, especificamente.
As regras para criar um objeto JSON são bem simples, bem simples mesmo, e bem poucas. Eu consigo identificar 3, apenas, ou 4 ou 5. Eu vou explicá-las através da revolucionária pedagogia do cão chupando manga.
Regra 1 UM abre/fecha chaves indica UM objeto, e o que está dentro do abre/fecha chaves descreve, CLARO, descreve o conteúdo do objeto.
Assim, o código JSON...
{
CÃO_CHUPANDO_MANGA
}
...descreve UM objeto cujo conteúdo é o cão chupando manga.
- Mas é objeto de que classe, professor?
De nenhuma. No formato JSON a gente descreve os objetos diretamente, explicitando informações e métodos lá contidos na forma comum de propriedades. Mas isso não é um objeto JSON ainda, porque a gente precisa se livrar do cão chupando manga. E pra isso a gente precisa das outras regrinhas.
Regra 2 Dentro de um abre/fecha chaves, descreve-se o conteúdo de um objeto, SEMPRE escrevendo o nome de uma propriedade, seguido de dois pontos, seguidos do valor daquela propriedade. Você pode escrever o nome da propriedade entre aspas duplas, você pode não escrever entre aspas duplas. Eu prefiro não escrever.
Assim, o código JSON...
{
nome: CÃO_CHUPANDO_MANGA ,
idade: CÃO_CHUPANDO_MANGA ,
enderecos: CÃO_CHUPANDO_MANGA
}
...descreve um objeto com as propriedades nome, idade e enderecos. Os diabos agora são os valores dessas propriedades. Vamos nos livrar deles. Dos diabos. Desses, ao menos.
Regra 3 Os valores, aqueles depois dos dois pontos, podem ser literais string, números, booleanos ou outros objetos delimitados por { }.
Assim, o código JSON...
{
nome: "Chicó" ,
idade: 27 ,
enderecos: CÃO_CHUPANDO_MANGA
}
...estabelece valores para as propriedades nome e idade. Falta a propriedade enderecos.
Regra 4 Valores também podem ser, na verdade, vetores. UM abre/fecha colchetes indica, ora vejam, UM vetor, e dentro do abre/fecha colchetes se descrevem os valores do vetor, separados, ora vejam, por vírgula.
Vamos por um vetor na propriedade enderecos. Aqui vai...
{
nome: "Chicó" ,
idade: 27 ,
enderecos: [CÃO_CHUPANDO_MANGA, CÃO_CHUPANDO_MANGA, CÃO_CHUPANDO_MANGA]
}
- Ah, mas agora ficamos com uma ruma de cão chupando manga de novo...
Sim, mas esses chifrudos dentro do vetor são valores, e a eles aplica-se a Regra 3. Podemos reescrevê-los assim...
{
nome: "Chicó" ,
idade: 27 ,
enderecos: [10, 77, 47]
}
Nesse caso enderecos seria um vetor/array de inteiros, o que é ilustrativo mas não é bem o que a gente espera de um vetor identificado por enderecos. Podemos escrever, ainda, já que JS é um mundo selvagem...
{
nome: "Chicó" ,
idade: 27 ,
enderecos: ["Viva", 5, true]
}
Eu não estou dizendo que é bonito, mas está dentro das regras. Mas como o mundo selvagem dá liberdade da gente fazer as coisas mais ou menos certas, vamos melhorar esse objeto...
{
nome: "Chicó" ,
idade: 27 ,
enderecos: ["Rua da Compadecida 5, Taperoá/PB", "Rua da Padaria 19, Cabaceiras/PB"]
}
Regra 5 Os valores das propriedades também podem ser funções. Essa tava na cara, estamos falando de objetos, afinal de contas.
{
nome: "Chicó" ,
idade: 27 ,
enderecos: ["Rua da Compadecida 5, Taperoá/PB", "Rua da Padaria 19, Cabaceiras/PB"]
maiorDeIdade: function(){
return this.idade>=18
}
}
- Ah, professor, e se a gente quisesse um array desse objeto que a gente acabou de descrever?
Simples, empurre os vários objetos entre colchetes, separados por vírgula...
[{
nome: "Chicó" ,
idade: 27 ,
enderecos: ["Rua da Compadecida 5, Taperoá/PB", "Rua da Padaria 19, Cabaceiras/PB"]
maiorDeIdade: function(){
return this.idade>=18
}
},
{
nome: "João Grilo" ,
idade: 29 ,
enderecos: ["Rua da Compadecida 57, Taperoá/PB", "Rua da Tapa 99, Serra Branca/PB"]
maiorDeIdade: function(){
return this.idade>=18
}
},
{
nome: "Major Antônio Morais" ,
idade: 65 ,
enderecos: ["Rua da Fazenda sn, Taperoá/PB", "Rua da Prata 1, Campina Grande/PB"]
maiorDeIdade: function(){
return this.idade>=18
}
}]
- Sim, mas aí a gente teria esse método maiorDeIdade replicado uma ruma de vez. Parece sem futuro.
Nesse caso, é mesmo. Escreva funções em objetos JSON com bastante moderação.
É que o JSON é, essencialmente, uma liguagem e intercâmbio de informação. São objetos "leves". Dá pra compreendê-los facilmente, convertê-los facilmente em texto e, em seguida salvá-los facilmente num arquivo ou enviá-los facilmente pela Internet. Um banco de dados NoSQL ou uma API de algum site pode te dar um texto JSON como resposta a alguma requisição. Esse texto pode facilmente ser convertido em objetos JS para ser processado e transformado em alguma interface com o usuário.
E a gente vai entender como processar objetos JSON.
Note que, apesar do nome JavaScript Object Notation, o formato JSON é independente de linguagem. Qualquer biblioteca mequetrefe converte um texto no formato JSON facilmente para objetos de uma linguagem específica.
No caso dos nossos scripts JS em páginas web, quando usamos a sintaxe JSON diretamente numa atribuição, já estaremos declarando objetos JavaScript e poderemos lidar com eles normalmente. Se recebermos um texto no formato JSON de uma consulta a algum site ou banco de dados, precisamos chamar um método para fazer a conversão daquele texto em objetos JS.
Agora vamos aplicar essas regras na resolução de um problema besta, mas didático.
Edite e salve o seguinte código html como ponto de partida.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<div><button id="botaoCarregar">Carregando cervejas...</button></div>
<div id="cervejasDiv"></div>
</body>
<script>
let cervejas = ["Guiness", "Desperados", "Becks"]
//cs é um array de cervejas
const carregarDiv = cs => {
const div = document.getElementById("cervejasDiv")
const itensHtml = cs.map( item => `<div>${item}</div>` )
div.innerHTML = `${itensHtml.join("\n")}`
}
let botao = document.getElementById("botaoCarregar")
botao.addEventListener("click", () => carregarDiv(cervejas) )
</script>
</html>
É um código bastante parecido com receita anterior. Se estiver com dificuldade de entendê-lo, talvez seja interessante revê-la.
Aberto através de um navegador, esse código exibe um botão rotulado por "Carregando cervejas".
Quando acionado o botão, são exibidos 3 nomes de cervejas, um abaixo do outro.
Que tal a gente transformar esse vetor cervejas num vetor de objetos? Lembrando da regra 1, aquela de botar objetos entre chaves, nossa primeira tentação seria escrever o vetor como...
let cervejas = [{"Guiness"}, {"Desperados"}, {"Becks"}]
Sendo que isso realmente não respeita alguma outra regra. Porque as informações dos objetos devem estar devidamente identificadas no formato JSON. Vamos consertar isso, aplicando a Regra 2.
Reescreva a linha do vetor para que fique como a seguir.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<div><button id="botaoCarregar">Carregando cervejas...</button></div>
<div id="cervejasDiv"></div>
</body>
<script>
let cervejas = [{name: "Guiness"}, {name: "Desperados"}, {name: "Becks"}]
//cs é um array de cervejas
const carregarDiv = cs => {
const div = document.getElementById("cervejasDiv")
const itensHtml = cs.map( item => `<div>${item}</div>` )
div.innerHTML = `${itensHtml.join("\n")}`
}
let botao = document.getElementById("botaoCarregar")
botao.addEventListener("click", () => carregarDiv(cervejas) )
</script>
</html>
Muito bonito. A questão é que quando a gente aciona o botão, agora, temos algo como na imagem a seguir...
Isso acontece porque, agora cada item do array cervejas é um objeto.
Por conseguinte, na expressão...
const itensHtml = cs.map( item => `<div>${item}</div>` )
aquele item é um objeto injetado num string. O comportamento default, nesse caso, é que se execute o método toString() do objeto. Como você não definiu nenhuma propriedade toString em nenhum dos objetos do seu array, vai ser injetado esse valor genérico no template string `<div>${item}</div>` para cada um deles. Vamos apresentar essa solução com toString apenas para mostrar como incorporar métodos/funções a objetos JSON, mas não deveremos usar essa solução.
Modifique o código.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<div><button id="botaoCarregar">Carregando cervejas...</button></div>
<div id="cervejasDiv"></div>
</body>
<script>
let cervejas = [
{
name: "Guiness",
toString: function(){
return this.name
}
},
{
name: "Desperados",
toString: function(){
return this.name
}
},
{
name: "Becks",
toString: function(){
return this.name
}
}
]
//cs é um array de cervejas
const carregarDiv = cs => {
const div = document.getElementById("cervejasDiv")
const itensHtml = cs.map( item => `<div>${item}</div>` )
div.innerHTML = `${itensHtml.join("\n")}`
}
let botao = document.getElementById("botaoCarregar")
botao.addEventListener("click", () => carregarDiv(cervejas) )
</script>
</html>
Carregando a página e acionando o botão, teremos o que esperamos.
Não vai ser uma boa solução replicar essa função em todos os objetos. Temos acesso direto às suas propriedades na montagem dos elementos de interface gráfica (as marcas html), ali na expressão `<div>${item}</div>`, e vamos fazer esse acesso.
Modifique e salve seu código.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<div><button id="botaoCarregar">Carregando cervejas...</button></div>
<div id="cervejasDiv"></div>
</body>
<script>
let cervejas = [
{
name: "Guiness",
},
{
name: "Desperados",
},
{
name: "Becks",
}
]
//cs é um array de cervejas
const carregarDiv = cs => {
const div = document.getElementById("cervejasDiv")
const itensHtml = cs.map( item => `<div>${item.name}</div>` )
div.innerHTML = `${itensHtml.join("\n")}`
}
let botao = document.getElementById("botaoCarregar")
botao.addEventListener("click", () => carregarDiv(cervejas) )
</script>
</html>
Recarregue a página, acione o botão e veja que o resultado é o mesmo do código anterior, mas há uma diferença substancial na codificação, em si: Não dependemos do que é retornado pelos toString() para montar nossa interface. Acessamos diretamente a propriedade que queremos exibir com a expressão item.name.
Com isso, se acrescentarmos uma propriedade alcohol aos objetos do array, não precisamos modificar todos os métodos toString para fazer os novos valores aparecerem na interface com o usuário. Basta empurrar item.alcohol em algum lugar e ser feliz.
Empurre item.alcohol em algum lugar - bem, em algum lugar do código - e seja feliz.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<div><button id="botaoCarregar">Carregando cervejas...</button></div>
<div id="cervejasDiv"></div>
</body>
<script>
let cervejas = [
{
name: "Guiness",
alcohol: "10%"
},
{
name: "Desperados",
alcohol: "6%"
},
{
name: "Becks",
alcohol: "5%"
}
]
//cs é um array de cervejas
const carregarDiv = cs => {
const div = document.getElementById("cervejasDiv")
const itensHtml = cs.map( item => `<div>${item.name} -- ${item.alcohol}</div>` )
div.innerHTML = `${itensHtml.join("\n")}`
}
let botao = document.getElementById("botaoCarregar")
botao.addEventListener("click", () => carregarDiv(cervejas) )
</script>
</html>
E lá está nossa página com mais uma informação.
Desnecessário ressaltar isso, mas eu vou ressaltar. Você pode acrescentar quantas propriedades quiser nos objetos. A notação do ponto, comum em diversas linguagens, é usada para acessar as propriedades que, nos objetos JSON especificamente, até agora, até onde eu sei e até onde eu vi, são todas públicas, sejam representações de funções ou de informações.
Há outras formas de acessar os atributos dos objetos além da notação do ponto. Vamos explorar duas delas. Nenhuma vai alterar a interface da página, apenas a maneira de fazer acesso aos itens do array de objetos.
Modifique o código e salve-o.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<div><button id="botaoCarregar">Carregando cervejas...</button></div>
<div id="cervejasDiv"></div>
</body>
<script>
let cervejas = [
{
name: "Guiness",
alcohol: "10%"
},
{
name: "Desperados",
alcohol: "6%"
},
{
name: "Becks",
alcohol: "5%"
}
]
//cs é um array de cervejas
const carregarDiv = cs => {
const div = document.getElementById("cervejasDiv")
const itensHtml = cs.map( ({name,alcohol}) => `<div>${name} -- ${alcohol}</div>` )
div.innerHTML = `${itensHtml.join("\n")}`
}
let botao = document.getElementById("botaoCarregar")
botao.addEventListener("click", () => carregarDiv(cervejas) )
</script>
</html>
Como sempre, mudamos apenas a parte em negrito, em relação ao código anterior.
Nossa função arrow function continua, obviamente, recebendo um objeto como parâmetro - é aquele bicho ali entre as chaves, o {name,alcohol}.
- Mas peraí, professor, isso não fere aquela Regra 2, a de ter um nome, dois pontos, e um valor???
Calma, jovem atleta de vídeo-game, essas chaves usadas aí, e como estão usadas aí, não indicam a criação de um objeto JSON e, por isso, não segue a sintaxe para criação de objetos JSON.
Essa expressão denota uma operação de desestruturação, tradução do inglês, destructuring, e pode realmente confundir a cabeça da gente.
Mas vamos ver o que mudou e entender o que faz esse tipo de construto.
Primeiro, empurramos um abre/fecha parênteses abrangendo o parâmetro.
cs.map( ({name,alcohol}) => `<div>${name} -- ${alcohol}</div>` )
É que, pelas regras de sintaxe das arrow functions, a gente só é liberado de escrever os parânteses não quando há um só parâmetro, como é o caso, mas quando há um só parâmetro expresso por uma só variável, como NÃO é o caso.
Segundo, listamos entre chaves as propriedades que sabemos que constam nos objetos que a arrow function recebe como parâmetro.
cs.map( ({name,alcohol}) => `<div>${name} -- ${alcohol}</div>` )
Isso é, efetivamente, a tal desestruturação. Estamos arregaçando o objeto e pegando duas propriedades suas para serem usadas, dali em diante, como variáveis independentes, SEM a necessidade do dot notation.
cs.map( ({name,alcohol}) => `<div>${name} -- ${alcohol}</div>` )
Você pode preferir o dot notation, receber item como parâmetro e acessar os atributos com item.name e item.alcohol. Mas muita gente não prefere o dot notation e um bom programador precisa compreender o código de muita gente.
Interessante notar que estamos arregaçando o objeto para ir buscar duas propriedades específicas que são de nosso interesse. Se esse objeto tivesse 30 propriedades e estivéssemos interessados apenas em name e alcohol, a expressão continuaria do mesmo jeito - especificamos nas desestruturação tão somente o que nos interessa, não precisamos escrever todas as propriedades do objeto. E esse processo não "danifica" o objeto, apesar de eu ter usado um termo, arregaçar, que possa dar a entender isso. A desestrutução, assim, é apenas um meio moderninho de se acessar propriedades de objetos, ninguém vai se ferir no meio do caminho. Usar uma das variáveis desestruturadas do lado esquerdo de uma atribuição (name = "Nova Cerveja") tampouco vai alterar a propriedade do objeto original. Ou seja: use desestruturação para facilitar o acesso a propriedades, pro seu código ficar mais fácil de escrever e de ler, mas não invente demais. Se quiser ser criativo, escreva um poema.
Há, naturalmente, outras formas de desestruturação no JS, e vamos abordá-las oportunamente.
Agora vamos a uma outra alternativa de acesso aos atributos dos objetos, desta feita sem desestruturação.
Modifique seu código e salve-o.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<div><button id="botaoCarregar">Carregando cervejas...</button></div>
<div id="cervejasDiv"></div>
</body>
<script>
let cervejas = [
{
name: "Guiness",
alcohol: "10%"
},
{
name: "Desperados",
alcohol: "6%"
},
{
name: "Becks",
alcohol: "5%"
}
]
//cs é um array de cervejas
const carregarDiv = cs => {
const div = document.getElementById("cervejasDiv")
const itensHtml = cs.map( item => `<div>${item['name']} -- ${item['alcohol']}</div>` )
div.innerHTML = `${itensHtml.join("\n")}`
}
let botao = document.getElementById("botaoCarregar")
botao.addEventListener("click", () => carregarDiv(cervejas) )
</script>
</html>
Agora, voltamos a receber o objeto item como parâmetro, mas não estamos acessando suas propriedades através do dot notation.
Neste caso, estamos acessando as propriedades indexando o objeto com um string representando o nome da propriedade (ora com 'name', ora com 'alcohol'. É parecido com o que fazemos com arrays, sendo que em vez de passar um inteiro como índice.
Isso acontece porque os objetos JS, como esses objetos JSON, podem ser tratados como "mapas", ou dicionários. Aquelas estruturas de dados que mapeiam chaves em valores. As chaves são os nomes das propriedades, os valores são o que quer que as propriedades sejam (inteiro, string, função, outro objeto etc.), e o acesso se dá através da indexação tão qual vimos no exemplo.
Chave de pesquisa sobre acesso a propriedades de objetos JSON: js how to access object property
3 Maneiras de acessar propriedades de objetos JSON
Modifique seu código para gerar uma tabela em vez desses divs xibatas. Use os cabeçalhos Nome e Álcool como cabeçalho das colunas.
Use estilos css para bonitificar sua tabela. Você pode bonitificar divs, se quiser, desde que a aparência final fique decente.
Acrescente duas propriedades aos objetos do vetor cervejas. Ambas as novas propriedades devem ser strings. A primeira, identifique-a por style e a segunda por ibu. Atribua valores a essas propriedades. Valores usuais para ibu (o amargor da cerveja) são números entre 15 e 60 (pode tratar como string, você não vai precisar fazer conta com esses números). Valores usuais para style incluem "Red Ale", "English IPA", "Imperial Stout", "Cream Ale", "Lager", "APA", "Pilsen". Distribua os valores como quiser, não se preocupe com a coerência em relação ao nome das cervejas. Em seguida, modifique sua função carregarDiv de forma que ela exiba as novas informações, bonitinhas, na página, junto com as demais informações.
Há uma limitação na sua função carregar. Ela processa adequadamente um array de cervejas mas só carrega o resultado num elemento de página identificado por cervejasDiv. Deixe sua função mais genérica, permitindo que o resultado do processamento seja carregado num elemento de id arbitrário. Faça também com que o valor default do id do elemento onde a tabela é carregada seja "cervejasDiv". Essa pesquisa pode ajudar nessa tarefa https://www.google.com/search?q=js+function+default+parameter
Há outra limitação na sua função carregarDiv. A essa altura, os nomes dos cabeçalhos das colunas são fixos. Conserte isso, permitindo que os nomes dos cabeçalhos das colunas sejam arbitrários. Deixe os valores default dos cabeçalhos como "Nome", "Álcool", "Estilo" e "Amargor"
Mas ainda há outra limitação na sua função carregarDiv. É que as propriedades que ela processa dos objetos são fixas. Se você passar um vetor de objetos que não têm exatamente as propriedades identificadas por name, alcohol, style e ibu, sua função não funciona adequadamente. Modifique sua função carregarDiv, contornando essa deficiência. Deixe os valores default das propriedades e serem processadas como name, alcohol, style e ibu.