Skip to content

Storage y JSON ​

¿Recargaste la página y tu lista de tareas desapareció? Las variables de JavaScript viven en memoria y se borran al cerrar o recargar la pestaña. En esta lección aprenderás a guardar datos en el navegador con localStorage y sessionStorage, y a convertir objetos en texto con JSON, el formato con el que se comunican prácticamente todas las APIs del mundo.

Concepto ​

JSON ​

JSON (JavaScript Object Notation) es un formato de texto para representar datos. Se parece muchísimo a los objetos de JavaScript, pero es sólo texto: puede viajar por internet, guardarse en un archivo o en el storage.

Piensa en JSON como empacar una mudanza: tus objetos (muebles) se desarman y se meten en cajas (texto) para transportarlos; al llegar se vuelven a armar.

json
{
  "id": 1,
  "nombre": "Ana",
  "activo": true,
  "intereses": ["diseño", "café"],
  "direccion": { "ciudad": "CDMX" },
  "telefono": null
}

Reglas de JSON (más estrictas que un objeto JS):

  • Las claves siempre van entre comillas dobles.
  • Los strings sólo con comillas dobles ("hola", nunca 'hola').
  • Sin comas al final del último elemento.
  • Sin funciones, undefined, fechas (Date) ni comentarios.
  • Valores permitidos: string, número, booleano, null, array y objeto.

Web Storage ​

El navegador te da dos "cajones" para guardar texto por cada sitio (dominio):

localStoragesessionStorage
DuraciónPermanente (hasta que se borre)Mientras la pestaña esté abierta
Compartido entre pestañasSíNo, cada pestaña tiene el suyo
Capacidad aproximada~5 MB~5 MB
Uso típicoPreferencias, carrito, borradoresDatos de un proceso de varios pasos

JSON.stringify y JSON.parse ​

js
const usuario = {
  nombre: 'Ana',
  edad: 28,
  intereses: ['diseño', 'café'],
  saludar() { return 'hola'; },   // las funciones se ignoran
  apodo: undefined                // undefined se ignora
};

// Objeto → texto JSON
const texto = JSON.stringify(usuario);
// '{"nombre":"Ana","edad":28,"intereses":["diseño","café"]}'

// Con formato legible (2 espacios de sangría), útil para depurar
console.log(JSON.stringify(usuario, null, 2));

// Sólo algunas propiedades
JSON.stringify(usuario, ['nombre', 'edad']); // '{"nombre":"Ana","edad":28}'

// Texto JSON → objeto
const deVuelta = JSON.parse(texto);
console.log(deVuelta.intereses[0]); // 'diseño'

Fechas en JSON ​

js
const pedido = { id: 1, fecha: new Date('2026-10-18T10:00:00Z') };
const json = JSON.stringify(pedido);
// '{"id":1,"fecha":"2026-10-18T10:00:00.000Z"}'  ← la fecha se volvió string

const recuperado = JSON.parse(json);
typeof recuperado.fecha;             // 'string'
const fecha = new Date(recuperado.fecha); // vuelve a ser Date

JSON inválido: siempre con try/catch ​

JSON.parse lanza un error si el texto no es JSON válido, y eso detiene tu programa:

js
JSON.parse("{nombre: 'Ana'}"); // ❌ SyntaxError: claves sin comillas dobles
JSON.parse('');                  // ❌ SyntaxError
JSON.parse('null');              // ✅ null

function parsearSeguro(texto, valorPorDefecto = null) {
  try {
    return JSON.parse(texto) ?? valorPorDefecto;
  } catch {
    return valorPorDefecto;
  }
}

parsearSeguro('esto no es json', []); // []

localStorage: lo básico ​

js
// Guardar (clave y valor SIEMPRE se guardan como string)
localStorage.setItem('tema', 'oscuro');

// Leer (devuelve null si la clave no existe)
const tema = localStorage.getItem('tema'); // 'oscuro'

// Eliminar una clave
localStorage.removeItem('tema');

// Borrar TODO lo de tu sitio
localStorage.clear();

// Cuántas claves hay y recorrerlas
for (let i = 0; i < localStorage.length; i++) {
  const clave = localStorage.key(i);
  console.log(clave, localStorage.getItem(clave));
}

sessionStorage tiene exactamente los mismos métodos.

Error común: guardar objetos sin JSON

js
localStorage.setItem('usuario', { nombre: 'Ana' });
localStorage.getItem('usuario'); // '[object Object]' 😱

Todo se convierte a string. Para objetos y arrays usa JSON.stringify al guardar y JSON.parse al leer.

Lo mismo con números y booleanos: localStorage.getItem('visitas') devuelve '5' (string), y 'false' es un string truthy.

Guardar objetos y arrays ​

js
const carrito = [
  { id: 2, nombre: 'Mouse', precio: 349, cantidad: 2 },
  { id: 5, nombre: 'Audífonos', precio: 899, cantidad: 1 }
];

// Guardar
localStorage.setItem('carrito', JSON.stringify(carrito));

// Leer (con valor por defecto si no existe)
const guardado = JSON.parse(localStorage.getItem('carrito')) ?? [];

Un módulo de storage seguro y reutilizable ​

En proyectos reales, localStorage puede fallar: navegación privada en algunos navegadores, almacenamiento lleno (QuotaExceededError), cookies bloqueadas, o datos corruptos que alguien editó a mano en DevTools. Envuelve todo en try/catch una sola vez:

js
// storage.js
const PREFIJO = 'miapp:';   // evita choques con otras apps del mismo dominio

export const storage = {
  get(clave, valorPorDefecto = null) {
    try {
      const texto = localStorage.getItem(PREFIJO + clave);
      return texto === null ? valorPorDefecto : JSON.parse(texto);
    } catch (error) {
      console.warn(`No se pudo leer "${clave}"`, error);
      return valorPorDefecto;
    }
  },

  set(clave, valor) {
    try {
      localStorage.setItem(PREFIJO + clave, JSON.stringify(valor));
      return true;
    } catch (error) {
      console.warn(`No se pudo guardar "${clave}"`, error);
      return false;
    }
  },

  remove(clave) {
    try {
      localStorage.removeItem(PREFIJO + clave);
    } catch {
      /* ignorar */
    }
  }
};

// Uso
storage.set('preferencias', { tema: 'oscuro', idioma: 'es' });
const prefs = storage.get('preferencias', { tema: 'claro', idioma: 'es' });

Buena práctica: versiona tus datos

Si mañana cambias la estructura (por ejemplo, de precio a precioCentavos), los datos viejos del navegador de tus usuarios romperán la app. Guarda una versión: { version: 2, items: [...] } y migra o descarta si no coincide.

Caso real: tema oscuro que se recuerda ​

js
const boton = document.querySelector('#toggle-tema');

// 1. Al cargar: preferencia guardada o la del sistema operativo
const guardado = storage.get('tema');
const prefiereOscuro = window.matchMedia('(prefers-color-scheme: dark)').matches;
let tema = guardado ?? (prefiereOscuro ? 'oscuro' : 'claro');

function aplicarTema() {
  document.documentElement.dataset.tema = tema; // <html data-tema="oscuro">
  boton.textContent = tema === 'oscuro' ? '☀️ Modo claro' : '🌙 Modo oscuro';
}

boton.addEventListener('click', () => {
  tema = tema === 'oscuro' ? 'claro' : 'oscuro';
  storage.set('tema', tema);
  aplicarTema();
});

aplicarTema();
css
:root { --fondo: #ffffff; --texto: #0f172a; }
[data-tema="oscuro"] { --fondo: #0f172a; --texto: #e2e8f0; }
body { background: var(--fondo); color: var(--texto); }
Tema y notas con localStorage

Caso real: borrador de formulario ​

Si el usuario cierra la pestaña por accidente, recupera lo que estaba escribiendo:

js
const form = document.querySelector('#contacto');

// Restaurar
const borrador = storage.get('borrador-contacto', {});
for (const [nombre, valor] of Object.entries(borrador)) {
  if (form.elements[nombre]) form.elements[nombre].value = valor;
}

// Guardar mientras escribe
form.addEventListener('input', () => {
  storage.set('borrador-contacto', Object.fromEntries(new FormData(form)));
});

// Limpiar al enviar
form.addEventListener('submit', () => storage.remove('borrador-contacto'));

Sincronizar entre pestañas ​

El evento storage se dispara en las otras pestañas del mismo sitio cuando cambia localStorage:

js
window.addEventListener('storage', (e) => {
  if (e.key === 'miapp:carrito') {
    carrito = JSON.parse(e.newValue) ?? [];
    renderCarrito(); // el carrito se actualiza en todas las pestañas
  }
});

sessionStorage: procesos de varios pasos ​

js
// Paso 1 del checkout: guardar la dirección
sessionStorage.setItem('checkout-direccion', JSON.stringify({ cp: '44100', calle: 'Juárez 123' }));

// Paso 2: leerla. Si el usuario cierra la pestaña, se descarta sola.
const direccion = JSON.parse(sessionStorage.getItem('checkout-direccion'));

Cookies vs. storage: ¿qué uso? ​

CookieslocalStorage / sessionStorage
Se envían al servidorSí, en cada peticiónNo
Capacidad~4 KB~5 MB
Accesibles desde JSSí (excepto HttpOnly)Sí, siempre
ExpiraciónConfigurablePermanente / al cerrar pestaña
Uso idealSesión de login (con HttpOnly y Secure)Preferencias, carrito, caché, borradores

Seguridad

Nunca guardes contraseñas, datos de tarjetas ni información sensible en localStorage. Cualquier script que se ejecute en tu página (incluido uno inyectado por XSS o una librería comprometida) puede leerlo. Los tokens de sesión, idealmente, en cookies HttpOnly gestionadas por el servidor.

Ejercicios ​

  1. Guarda en localStorage cuántas veces el usuario ha visitado la página y muéstralo: "Esta es tu visita número 5".
  2. Escribe una función guardarFavorito(id) que agregue un id a un array de favoritos en storage sin duplicarlo, y esFavorito(id).
  3. Convierte este texto JSON en objeto y muestra el nombre del segundo producto: '{"productos":[{"nombre":"Mouse"},{"nombre":"Teclado"}]}'.
  4. Haz que un <textarea> guarde automáticamente su contenido y lo restaure al recargar.
  5. Escribe limpiarCaducados(): guarda elementos como { valor, expira: timestamp } y elimina los que ya expiraron.
Ver solución
js
// 1
const visitas = Number(localStorage.getItem('visitas') ?? 0) + 1;
localStorage.setItem('visitas', visitas);
document.querySelector('#visitas').textContent = `Esta es tu visita número ${visitas}`;

// 2
function guardarFavorito(id) {
  const favoritos = storage.get('favoritos', []);
  if (!favoritos.includes(id)) storage.set('favoritos', [...favoritos, id]);
}
const esFavorito = (id) => storage.get('favoritos', []).includes(id);

// 3
const datos = JSON.parse('{"productos":[{"nombre":"Mouse"},{"nombre":"Teclado"}]}');
console.log(datos.productos[1].nombre); // 'Teclado'

// 4
const area = document.querySelector('#notas');
area.value = localStorage.getItem('notas') ?? '';
area.addEventListener('input', () => localStorage.setItem('notas', area.value));

// 5
function guardarConExpiracion(clave, valor, minutos) {
  storage.set(clave, { valor, expira: Date.now() + minutos * 60000 });
}
function limpiarCaducados() {
  Object.keys(localStorage)
    .filter((k) => k.startsWith('miapp:'))
    .forEach((k) => {
      const item = storage.get(k.replace('miapp:', ''));
      if (item?.expira && item.expira < Date.now()) localStorage.removeItem(k);
    });
}

Resumen ​

  • JSON es texto con reglas estrictas: comillas dobles, sin funciones ni undefined.
  • JSON.stringify convierte objetos en texto; JSON.parse hace lo contrario (envuélvelo en try/catch).
  • localStorage persiste para siempre; sessionStorage sólo mientras la pestaña está abierta. Ambos guardan sólo strings.
  • Crea un pequeño módulo de storage con prefijo, valores por defecto y try/catch.
  • No guardes información sensible en el storage.

Siguiente: Asincronía

Academia Web — aprende, copia y construye.