← JavaScript: linguagem e diagnóstico de aplicações
06 / 10 · 50 MIN

JSON, texto e contratos de tempo

Preserva identidade e significado ao atravessar JSON, texto Unicode e datas com offsets explícitos.

JSON tem uma gramática própria

JSON não é um literal JavaScript arbitrário. Os nomes das propriedades usam aspas duplas, não há vírgula final e undefined não é um literal aceite. A raiz pode ser um valor primitivo, pelo que uma leitura sem exceção não demonstra que obtiveste um registo com os campos necessários.

O produtor deve definir também a representação dos dados. Um ID com até 30 dígitos e zeros iniciais significativos deve ser transportado como string se esses detalhes fazem parte da identidade. Ler um número JSON como Number e só depois chamar String não recupera informação perdida. A sintaxe BigInt com sufixo n não é uma alternativa válida dentro de JSON.

Serializar não preserva todos os valores

Sem personalizações, JSON.stringify omite propriedades de objeto com undefined e usa null para essas posições em arrays. NaN e infinitos são representados como null. Um BigInt sem conversão apropriada provoca TypeError; uma estrutura cíclica também não é representada diretamente. Estas regras devem ser consideradas antes de escolher JSON para guardar uma fotografia de dados.

Um Date válido usa toJSON e passa a texto. Ao ler esse JSON sem reviver, o resultado continua a ser string, não uma instância Date. Uma cópia que perde uma propriedade undefined, transforma NaN em null e converte uma data em texto pode ser adequada a um contrato de intercâmbio, mas não conserva automaticamente todos os tipos da origem.

Transformações durante a leitura

O reviver de JSON.parse recebe valores durante a reconstrução, incluindo valores de propriedades e a raiz. Devolver undefined para uma propriedade de objeto elimina-a. Para conservar o valor sem alteração, é necessário devolver esse valor. A chamada da raiz usa a chave vazia; se o reviver devolver undefined nessa chamada, o resultado global também será undefined.

Uma política que converte todas as strings parecidas com datas pode alterar identificadores ou textos que não eram instantes. Prefere regras ligadas a campos e a um esquema conhecido. Mesmo quando usas reviver, mantém a validação explícita do resultado final: transformar valores e demonstrar que o documento cumpre o contrato são responsabilidades distintas.

Texto: unidades, pontos e equivalência

String.length mede unidades UTF-16. O símbolo 💡 ocupa duas dessas unidades, mas a iteração normal da string produz um ponto de código. Um ponto de código também não equivale sempre a um carácter visual: uma sequência pode combinar uma letra com um acento ou vários componentes de um símbolo. Define a unidade que o limite do produto realmente pretende medir.

As representações "é" e "e" seguido de acento combinante podem ser canonicamente equivalentes e continuar diferentes numa igualdade estrita direta. Se a política pede equivalência canónica, compara resultados de normalize("NFC"). Não apliques essa transformação indiscriminadamente a tokens ou IDs cuja representação exata tenha significado contratual.

Expressões regulares com estado

Uma expressão regular com g ou y usa lastIndex como parte da procura. Reutilizar /^[A-Z]+$/g em duas chamadas test("AB") não equivale a duas validações independentes: o primeiro match avança lastIndex e a segunda procura pode falhar por começar depois do início exigido por ^.

Para uma regra de validação sem estado entre entradas, escolhe uma expressão sem essas flags quando elas não forem necessárias, ou define e aplica explicitamente a reposição de estado. Mantém também um contrato claro de formato completo; encontrar uma parte válida não é necessariamente aceitar a entrada inteira. Antes de atribuir uma falha intermitente aos dados, verifica se o validador reteve estado da chamada anterior.

Instantes, offsets e validade

Date.parse interpreta o formato normativo de data e hora e devolve milissegundos ou NaN. Uma data-hora desse formato sem offset é interpretada em hora local, enquanto uma data apenas é interpretada em UTC. Formatos regionais fora desse contrato podem depender do motor. Para representar um instante partilhado entre sistemas, exige uma data-hora com Z ou offset explícito.

toISOString exprime um Date válido em UTC com Z; não conserva o offset original como metadado. Num Date inválido lança RangeError. Distingue esse comportamento de toJSON, que devolve null para o valor temporal inválido normal. Se precisas de conservar a zona de origem ou a intenção de calendário, guarda essa informação separadamente do instante.

Exemplo: ordem temporal sem perda de identidade

O exemplo orderEvents assume que uma etapa anterior já validou IDs string e datas-horas ISO com offsets explícitos. Preserva id e at e acrescenta o valor time. A verificação Number.isFinite deteta uma interpretação temporal inválida, mas não substitui a validação anterior do formato e do esquema. A ordenação altera apenas o array preparado.

O evento "09" às 10:00 com +02:00 corresponde a 08:00Z e precede o evento "9" às 09:00Z. Ordenar diretamente as strings completas sugeriria a ordem contrária. Os IDs continuam distintos porque não foram convertidos em números. Conserva o texto original quando ele é necessário para explicar a origem do instante; a representação UTC normalizada pode ser um campo adicional.

function orderEvents(events) {
  const prepared = events.map(event => {
    const time = Date.parse(event.at);
    if (!Number.isFinite(time)) throw new RangeError("invalid-instant");
    return { id: event.id, at: event.at, time };
  });
  return prepared.sort((a, b) => a.time - b.time);
}
const events = [
  { id: "09", at: "2026-10-13T10:00:00+02:00" },
  { id: "9", at: "2026-10-13T09:00:00Z" }
];
NA PRÁTICA

O exemplo recebe registos já validados: IDs string e datas-horas ISO com offset explícito, num intervalo temporal operacional limitado. A função acrescenta uma verificação de valor temporal finito; não é um validador completo do formato ISO nem da identidade.

Armadilhas comuns

Tratar JSON como clonagem universal; usar length como número de caracteres visuais; reutilizar regex global numa validação; comparar datas com offsets diferentes como texto.

Tópicos relacionados: Valores, conversões e contratos numéricos · Arrays, coleções e iteração · Erros, validação e contratos de falha · Eventos, HTTP e cancelamento no navegador

Leva esta ideia contigo

O formato de intercâmbio faz parte do contrato. Conserva os dados originais necessários e torna explícitas conversões, perdas e regras temporais.

Criar conta

Referência: JSON.stringify · ECMAScript 2026 (17th edition); browser APIs identified separately