# Aula 2.5 - Objetos, JSON e modelagem

## Identificação

- **Duração:** 2 horas
- **Tipo:** teoria aplicada e laboratório
- **Entrega:** modelo de tarefa com importação e exportação JSON
- **Laboratório:** [`../exemplos/aula-2.5/index.html`](../exemplos/aula-2.5/index.html)

## Introdução

### O que é um objeto?

Um objeto JavaScript reúne informações relacionadas por meio de propriedades:

```js
const task = {
  id: 1,
  title: "Estudar objetos",
  status: "doing",
  estimate: 3,
};
```

Cada propriedade possui uma chave e um valor. Objetos são úteis para representar
entidades, configurações e resultados com campos nomeados.

### Objeto e array são a mesma coisa?

Não. Um objeto representa normalmente uma entidade:

```js
const task = {
  title: "Estudar",
  status: "todo",
};
```

Um array representa uma coleção ordenada:

```js
const tasks = [
  { title: "Estudar", status: "todo" },
  { title: "Praticar", status: "doing" },
];
```

É comum utilizar arrays de objetos porque temos várias entidades com o mesmo
formato.

### Objeto e classe são a mesma coisa?

Não. Um objeto é um valor concreto. Uma classe é uma forma de definir como objetos
podem ser construídos e quais comportamentos compartilham.

JavaScript permite criar objetos diretamente, sem classe:

```js
const settings = {
  theme: "dark",
};
```

Neste curso, começaremos com objetos literais e funções. Classes serão usadas apenas
quando trouxerem uma vantagem clara.

### O que é JSON?

JSON significa *JavaScript Object Notation*. É um formato textual usado para trocar
dados:

```json
{
  "title": "Estudar objetos",
  "status": "doing",
  "estimate": 3
}
```

JSON se parece com um objeto JavaScript, mas não é a mesma coisa:

- objeto JavaScript é um valor em memória;
- JSON é texto;
- JSON exige chaves entre aspas duplas;
- JSON não transporta funções;
- comentários e vírgulas finais não são permitidos no JSON padrão.

### JSON é um banco de dados?

Não. JSON é um formato. Ele pode ser enviado por uma API, armazenado em arquivo ou
guardado em uma coluna, mas não oferece sozinho consultas, relacionamentos,
transações e controle de acesso.

No projeto futuro, o navegador trocará JSON com a API, e a API persistirá os dados
no MySQL.

### Onde isso aparece no projeto?

Nesta aula, modelaremos uma tarefa com campos consistentes, criaremos cópias sem
alterar o objeto original e converteremos o modelo entre objeto e JSON. Dados
importados serão validados antes de entrar na aplicação.

## Objetivos

Ao final da aula, o aluno deverá conseguir:

1. criar e acessar objetos;
2. diferenciar objeto, array, classe, JSON e banco de dados;
3. adicionar, atualizar e remover propriedades;
4. compreender referências de objetos;
5. copiar objetos com spread;
6. reconhecer os limites de uma cópia rasa;
7. utilizar desestruturação e valores padrão;
8. aplicar optional chaining e nullish coalescing;
9. modelar uma entidade com campos coerentes;
10. serializar e interpretar JSON;
11. validar dados externos antes do uso.

## Pré-requisitos

- Aulas 2.1 a 2.4 concluídas;
- valores, tipos, funções e arrays;
- noções de formulário e DOM;
- navegador, DevTools e editor.

## Pergunta orientadora

> Como representar uma entidade de forma consistente e trocar seus dados sem confundir objeto em memória com texto JSON?

## Roteiro sugerido

| Etapa | Duração |
|---|---:|
| Objetos e propriedades | 25 min |
| Referência, cópia e desestruturação | 30 min |
| Modelagem de entidades | 20 min |
| JSON e validação | 25 min |
| Laboratório | 15 min |
| Revisão | 5 min |

## 1. Objeto literal

```js
const student = {
  name: "Ana",
  active: true,
  completedLessons: 4,
};
```

As chaves podem ser escritas sem aspas quando são identificadores válidos. Valores
podem ser strings, números, booleanos, arrays, outros objetos, funções ou valores
ausentes.

## 2. Acesso com ponto

```js
student.name;
student.active;
```

A notação de ponto é a opção mais clara quando conhecemos o nome da propriedade.

## 3. Acesso com colchetes

```js
student["name"];
```

Colchetes são necessários quando:

- o nome contém caracteres incompatíveis com a notação de ponto;
- a chave está guardada em uma variável;
- a propriedade é calculada dinamicamente.

```js
const selectedField = "completedLessons";
student[selectedField];
```

Não use:

```js
student.selectedField;
```

Isso procura literalmente uma propriedade chamada `selectedField`.

## 4. Criar e atualizar propriedades

```js
const task = {
  title: "Estudar",
};

task.status = "todo";
task.title = "Estudar objetos";
```

Mesmo declarado com `const`, o conteúdo do objeto pode mudar. `const` impede que a
variável passe a apontar para outro objeto:

```js
task = {}; // TypeError
```

## 5. Remover propriedades

```js
delete task.temporaryField;
```

Antes de remover, avalie se o campo deveria ser opcional ou receber `null`.
Estruturas consistentes são mais fáceis de validar e exibir.

## 6. Propriedades calculadas

```js
const fieldName = "priority";

const task = {
  title: "Estudar",
  [fieldName]: "high",
};
```

O valor de `fieldName` se torna a chave.

## 7. Atalhos de propriedade

Quando variável e chave têm o mesmo nome:

```js
const title = "Estudar objetos";
const status = "doing";

const task = {
  title,
  status,
};
```

Equivale a:

```js
const task = {
  title: title,
  status: status,
};
```

## 8. Métodos

Uma função armazenada em uma propriedade é um método:

```js
const task = {
  title: "Estudar",
  describe() {
    return `Tarefa: ${this.title}`;
  },
};
```

`this` depende da forma como a função é chamada. Regras puras que recebem dados por
parâmetro costumam ser mais simples de reutilizar:

```js
function describeTask(task) {
  return `Tarefa: ${task.title}`;
}
```

## 9. Objetos são acessados por referência

```js
const original = {
  title: "Estudar",
};

const alias = original;
alias.title = "Alterado";

console.log(original.title); // "Alterado"
```

`alias` e `original` apontam para o mesmo objeto.

Comparações também usam referência:

```js
{} === {}; // false

const task = {};
task === task; // true
```

Dois objetos com campos iguais não são automaticamente a mesma referência.

## 10. Cópia com spread

```js
const original = {
  title: "Estudar",
  status: "todo",
};

const updated = {
  ...original,
  status: "done",
};
```

`updated` é um novo objeto. `original.status` continua sendo `"todo"`.

A ordem importa:

```js
const wrongOrder = {
  status: "done",
  ...original,
};
```

O `status` do original sobrescreve `"done"`.

## 11. Cópia rasa

Spread copia apenas o primeiro nível:

```js
const original = {
  title: "Estudar",
  metadata: {
    source: "course",
  },
};

const copy = { ...original };
copy.metadata.source = "manual";

console.log(original.metadata.source); // "manual"
```

Para atualizar de maneira imutável:

```js
const updated = {
  ...original,
  metadata: {
    ...original.metadata,
    source: "manual",
  },
};
```

## 12. `structuredClone`

Para dados compatíveis, o navegador oferece cópia profunda:

```js
const copy = structuredClone(original);
```

`structuredClone` suporta vários tipos, mas não copia funções e pode falhar com
valores não clonáveis. Não utilize `JSON.stringify` seguido de `JSON.parse` como
solução geral de cópia profunda, pois tipos e valores podem ser perdidos.

## 13. Desestruturação

Extrai propriedades para variáveis:

```js
const task = {
  title: "Estudar",
  status: "doing",
};

const { title, status } = task;
```

Renomeando:

```js
const { title: taskTitle } = task;
```

Valor padrão:

```js
const { estimate = 1 } = task;
```

O padrão é usado quando a propriedade é `undefined`, não quando é `null`.

## 14. Rest em objetos

```js
const task = {
  id: 1,
  title: "Estudar",
  status: "doing",
};

const { id, ...editableFields } = task;
```

`editableFields` recebe uma cópia rasa das demais propriedades.

## 15. Optional chaining

```js
const city = user.address?.city;
```

Se `address` for `null` ou `undefined`, o resultado será `undefined` em vez de um
erro.

Não use optional chaining para esconder um campo obrigatório ausente. Ele é útil
quando a ausência é realmente permitida.

## 16. Nullish coalescing

```js
const estimate = task.estimate ?? 1;
```

O valor padrão é usado apenas para `null` ou `undefined`.

Compare:

```js
const valueWithOr = task.estimate || 1;
```

Se `estimate` for `0`, `||` escolherá `1`, enquanto `??` preservará `0`.

## 17. Verificar propriedades

```js
Object.hasOwn(task, "title");
```

Isso verifica se a propriedade pertence diretamente ao objeto.

Para listar:

```js
Object.keys(task);
Object.values(task);
Object.entries(task);
```

`Object.entries` produz pares `[chave, valor]`, úteis em iteração.

## 18. Modelagem de uma tarefa

Antes de programar, defina o significado dos campos:

```js
const task = {
  id: "task-001",
  title: "Estudar objetos",
  description: "",
  status: "todo",
  priority: "planned",
  estimateHours: 2,
  tags: ["javascript", "curso"],
  completedAt: null,
};
```

Decisões:

- `id` identifica a entidade;
- `title` é obrigatório;
- `description` pode ser string vazia;
- `status` aceita alternativas conhecidas;
- `estimateHours` é número não negativo;
- `tags` é um array de strings;
- `completedAt` usa `null` enquanto não concluída.

Um bom modelo reduz estados ambíguos.

## 19. Fábrica de objetos

```js
function createTask(input) {
  const title = input.title.trim();

  return {
    id: crypto.randomUUID(),
    title,
    description: input.description?.trim() ?? "",
    status: input.status ?? "todo",
    priority: input.priority ?? "planned",
    estimateHours: Number(input.estimateHours ?? 0),
    tags: input.tags ?? [],
    completedAt: null,
  };
}
```

A fábrica centraliza padrões e normalização. Isso não substitui validação.

## 20. Objeto JavaScript para JSON

```js
const jsonText = JSON.stringify(task);
```

Formatado para leitura:

```js
const jsonText = JSON.stringify(task, null, 2);
```

O terceiro argumento define a indentação.

Valores como `undefined`, funções e símbolos não são representados normalmente em
objetos JSON. Datas viram strings.

## 21. JSON para valor JavaScript

```js
const parsed = JSON.parse(jsonText);
```

`JSON.parse` pode lançar `SyntaxError`:

```js
try {
  const parsed = JSON.parse(jsonText);
} catch (error) {
  console.error("JSON inválido", error);
}
```

O resultado não deve ser confiado apenas porque o texto é JSON válido.

## 22. Sintaxe válida não garante modelo válido

Este JSON é sintaticamente válido:

```json
{
  "title": 42,
  "status": "qualquer-coisa"
}
```

Mas não respeita nosso modelo. Precisamos validar:

```js
function validateTask(value) {
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
    return false;
  }

  const hasValidTitle =
    typeof value.title === "string" &&
    value.title.trim().length >= 3;

  const allowedStatuses = ["todo", "doing", "done"];
  const hasValidStatus = allowedStatuses.includes(value.status);

  return hasValidTitle && hasValidStatus;
}
```

Em módulos posteriores, usaremos bibliotecas e contratos TypeScript para tornar
essa validação mais robusta.

## 23. JSON não executa código

JSON deve ser interpretado com `JSON.parse`, nunca com `eval`:

```js
const value = JSON.parse(jsonText);
```

Não use:

```js
const value = eval(`(${jsonText})`);
```

`eval` pode executar conteúdo malicioso e não é necessário para ler JSON.

## 24. Datas no JSON

```js
const task = {
  createdAt: new Date(),
};

const text = JSON.stringify(task);
```

A data se torna string ISO. Ao interpretar:

```js
const parsed = JSON.parse(text);
typeof parsed.createdAt; // "string"
```

Se a aplicação precisa de `Date`, converta explicitamente após validar o formato.

## 25. Laboratório guiado

Abra o [modelador de tarefas](../exemplos/aula-2.5/index.html).

### Etapa 1 — Crie o objeto

Preencha o formulário e clique em **Criar modelo**. Observe:

- normalização do título;
- conversão da estimativa;
- transformação das tags em array;
- campos padrão;
- JSON formatado.

### Etapa 2 — Atualize sem mutar

Marque a tarefa como concluída. O laboratório cria um novo objeto com spread e
preserva a versão anterior.

### Etapa 3 — Importe JSON

Edite o JSON e clique em **Validar e importar**. Teste:

1. JSON válido e modelo válido;
2. erro de sintaxe;
3. título numérico;
4. status não permitido;
5. objeto ausente ou array no lugar da tarefa.

### Etapa 4 — Inspecione o código

Localize:

- `createTask`;
- `completeTask`;
- `serializeTask`;
- `parseAndValidateTask`;
- `validateTask`;
- atualizações com spread.

## 26. Erros comuns

### Confundir referência com cópia

```js
const copy = original;
```

Isso cria um alias. Para copiar o primeiro nível:

```js
const copy = { ...original };
```

### Confiar em JSON externo

`JSON.parse` verifica sintaxe, não regras de negócio. Sempre valide estrutura e
tipos.

### Esquecer que JSON é texto

```js
jsonText.title; // undefined
```

Primeiro:

```js
const task = JSON.parse(jsonText);
task.title;
```

### Usar `||` quando zero é válido

```js
const estimate = input.estimate || 1;
```

Use `??` quando apenas ausência deve acionar o padrão.

### Espalhar dados não confiáveis sem filtrar

```js
const task = {
  ...externalValue,
};
```

Selecione e valide os campos permitidos. Não transporte propriedades arbitrárias
para entidades internas.

## 27. Boas práticas

- modele campos antes de construir a interface;
- use nomes consistentes;
- diferencie ausência, vazio e zero;
- preserve identificadores em atualizações;
- copie cada nível que será alterado;
- valide todo dado externo;
- trate erros de `JSON.parse`;
- use `JSON.stringify` para serializar;
- nunca utilize `eval` para JSON;
- não inclua segredos em JSON enviado ao navegador.

## 28. Exercícios

### Exercício 1 — Perfil

Crie um objeto `student` com nome, e-mail e aulas concluídas.

### Exercício 2 — Atualização

Crie uma nova versão de uma tarefa com `status: "done"` sem alterar a original.

### Exercício 3 — Desestruturação

Extraia `title` e `status`, renomeando `title` para `taskTitle`.

### Exercício 4 — Modelo aninhado

Atualize `metadata.source` copiando corretamente os dois níveis.

### Exercício 5 — JSON

Serialise um objeto, interprete o texto e valide os campos obrigatórios.

## 29. Desafio

Amplie o laboratório:

- permita importar um array de tarefas;
- valide cada item e informe o índice do erro;
- rejeite propriedades desconhecidas;
- adicione `createdAt` em formato ISO;
- converta a data validada para `Date` apenas na camada de aplicação;
- exporte somente campos públicos.

## 30. Lista de verificação de conclusão

- [ ] Sei diferenciar objeto, array, JSON e banco de dados.
- [ ] Consigo usar ponto e colchetes.
- [ ] Entendo que objetos usam referências.
- [ ] Sei criar uma atualização com spread.
- [ ] Consigo explicar cópia rasa.
- [ ] Uso desestruturação e valores padrão.
- [ ] Sei diferenciar `??` e `||`.
- [ ] Converto objeto para JSON e JSON para objeto.
- [ ] Valido dados depois de `JSON.parse`.
- [ ] Não utilizo `eval`.

## 31. Critérios de avaliação

| Critério | Pontos |
|---|---:|
| Modelo coerente e documentado | 25 |
| Atualizações sem mutação indevida | 20 |
| Serialização e interpretação JSON | 20 |
| Validação de dados externos | 25 |
| Interface e mensagens de erro | 10 |
| **Total** | **100** |

## Resumo

Nesta aula, aprendemos que:

- objetos representam dados por propriedades nomeadas;
- arrays representam coleções ordenadas;
- objetos são acessados por referência;
- spread cria uma cópia rasa;
- desestruturação facilita acessar campos;
- JSON é texto, não objeto e não banco de dados;
- `JSON.parse` verifica sintaxe, mas a aplicação ainda precisa validar o modelo.

## Próxima aula

Na Aula 2.6, separaremos o código em módulos com `import` e `export` e trataremos
falhas previsíveis com erros, exceções e estados de recuperação.
