JavaScript

JavaScript

Tutorial JavaScript: LocalStorage y SessionStorage

Descubre los límites y seguridad en el almacenamiento web para aplicaciones modernas. Aprende a usar APIs eficientemente con medidas de protección.

Aprende JavaScript y certifícate

Límites y consideraciones de seguridad

Al trabajar con las APIs de almacenamiento web (localStorage y sessionStorage), es fundamental comprender sus limitaciones técnicas y las implicaciones de seguridad que conllevan. Estos mecanismos, aunque útiles, no son soluciones universales para todos los escenarios de persistencia de datos.

Capacidad de almacenamiento

La capacidad de almacenamiento disponible en localStorage y sessionStorage está limitada por los navegadores. Aunque esta limitación varía según el navegador, generalmente se establece alrededor de 5-10 MB por dominio. Esta restricción es importante cuando planificamos qué datos almacenar:

// Función para comprobar el espacio utilizado en localStorage
function checkStorageUsage() {
  let totalSize = 0;
  for (let key in localStorage) {
    if (localStorage.hasOwnProperty(key)) {
      totalSize += localStorage[key].length * 2; // Caracteres * 2 bytes (UTF-16)
    }
  }
  return (totalSize / 1024).toFixed(2) + " KB";
}

console.log(`Espacio utilizado: ${checkStorageUsage()}`);

Cuando intentamos exceder este límite, el navegador lanzará una excepción. Es buena práctica implementar manejo de errores para estas situaciones:

try {
  // Intentamos almacenar un objeto grande
  const largeObject = { /* datos extensos */ };
  localStorage.setItem('largeData', JSON.stringify(largeObject));
} catch (e) {
  if (e instanceof DOMException && e.name === 'QuotaExceededError') {
    console.error('Error: Límite de almacenamiento excedido');
    // Implementar estrategia de gestión (eliminar datos antiguos, etc.)
  }
}

Políticas de mismo origen (Same-Origin Policy)

Una de las restricciones de seguridad más importantes es la política de mismo origen. Tanto localStorage como sessionStorage están vinculados al origen del documento, que se define por la combinación de:

  • Protocolo (http, https)
  • Dominio (ejemplo.com)
  • Puerto (80, 443, etc.)
// Este almacenamiento solo es accesible desde https://miapp.com:443
// No puede ser accedido desde http://miapp.com o https://otrositio.com
localStorage.setItem('userData', JSON.stringify({name: 'Ana'}));

Esta restricción tiene importantes implicaciones:

  • Los datos almacenados en https://app.ejemplo.com no son accesibles desde https://ejemplo.com
  • Subdominios diferentes se consideran orígenes distintos
  • El cambio de protocolo (HTTP a HTTPS) crea un origen diferente

Vulnerabilidades de seguridad

El almacenamiento web no está cifrado por defecto, lo que presenta varios riesgos de seguridad:

  • Ataques XSS: Un script malicioso inyectado en la página puede acceder a todo el almacenamiento del dominio:
// Código potencialmente vulnerable
function displayUserComment(comment) {
  // Peligroso: insertar contenido sin sanitizar
  document.getElementById('comments').innerHTML += comment;
  
  // Mejor enfoque: sanitizar el contenido
  const sanitizedComment = DOMPurify.sanitize(comment);
  document.getElementById('comments').innerHTML += sanitizedComment;
}
  • Persistencia de datos sensibles: Nunca debemos almacenar información confidencial sin cifrado:
// Enfoque incorrecto
localStorage.setItem('authToken', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...');

// Mejor enfoque (aunque no perfecto)
function encryptData(data, key) {
  // Implementación de cifrado (usando SubtleCrypto API)
  return encryptedData;
}

const encryptedToken = encryptData(token, secretKey);
localStorage.setItem('secureToken', encryptedToken);

Modo incógnito y limpieza de navegador

Es importante considerar que los datos en localStorage persisten incluso después de cerrar el navegador, pero pueden ser eliminados en estas situaciones:

  • Cuando el usuario borra los datos de navegación
  • En modo incógnito/privado, donde los datos se eliminan al cerrar la ventana
  • Si el usuario tiene configurado su navegador para rechazar cookies o almacenamiento local
// Detectar si localStorage está disponible
function isStorageAvailable() {
  try {
    const testKey = '__storage_test__';
    localStorage.setItem(testKey, testKey);
    localStorage.removeItem(testKey);
    return true;
  } catch (e) {
    return false;
  }
}

// Implementar alternativa si no está disponible
if (!isStorageAvailable()) {
  console.warn('localStorage no disponible, usando alternativa en memoria');
  // Implementar solución alternativa
}

Consideraciones de rendimiento

Aunque el acceso al almacenamiento web es síncrono, operaciones frecuentes o con grandes volúmenes de datos pueden afectar el rendimiento:

  • Las operaciones de lectura/escritura bloquean el hilo principal
  • La serialización/deserialización de objetos complejos puede ser costosa
// Enfoque ineficiente: múltiples operaciones individuales
for (let i = 0; i < 1000; i++) {
  localStorage.setItem(`item_${i}`, `value_${i}`);
}

// Enfoque más eficiente: operación única con estructura de datos
const batchData = {};
for (let i = 0; i < 1000; i++) {
  batchData[`item_${i}`] = `value_${i}`;
}
localStorage.setItem('batchItems', JSON.stringify(batchData));

Comprender estas limitaciones y consideraciones de seguridad es esencial para implementar soluciones de almacenamiento web robustas y seguras en aplicaciones JavaScript modernas.

Patrones de diseño: Implementación de wrapper classes y sistemas de caché

El uso directo de localStorage y sessionStorage puede volverse complejo cuando las aplicaciones crecen. Para mejorar la mantenibilidad, testabilidad y agregar funcionalidades avanzadas, es recomendable implementar patrones de diseño que encapsulen estas APIs nativas. Veremos cómo crear abstracciones elegantes que simplifiquen el trabajo con el almacenamiento web.

Wrapper classes para almacenamiento

Una clase wrapper proporciona una interfaz más amigable sobre las APIs nativas, añadiendo funcionalidades como manejo automático de JSON, expiración de datos y validación:

class StorageManager {
  constructor(storageType = 'local') {
    this.storage = storageType === 'local' ? localStorage : sessionStorage;
  }

  set(key, value, expiresIn = null) {
    const item = {
      value,
      timestamp: Date.now()
    };
    
    if (expiresIn) {
      item.expiresAt = Date.now() + expiresIn;
    }
    
    this.storage.setItem(key, JSON.stringify(item));
    return true;
  }

  get(key) {
    const item = this.storage.getItem(key);
    if (!item) return null;
    
    const parsedItem = JSON.parse(item);
    
    // Verificar expiración
    if (parsedItem.expiresAt && parsedItem.expiresAt < Date.now()) {
      this.remove(key);
      return null;
    }
    
    return parsedItem.value;
  }

  remove(key) {
    this.storage.removeItem(key);
  }

  clear() {
    this.storage.clear();
  }
}

Esta implementación ofrece varias ventajas:

  • Manejo automático de serialización/deserialización JSON
  • Expiración de datos configurable
  • Interfaz unificada para localStorage y sessionStorage
  • Encapsulación que facilita cambios futuros

Podemos utilizar esta clase de manera sencilla:

const userStorage = new StorageManager('local');

// Guardar datos con expiración de 1 hora
userStorage.set('userProfile', { name: 'Carlos', role: 'admin' }, 3600000);

// Recuperar datos
const profile = userStorage.get('userProfile');
console.log(profile); // { name: 'Carlos', role: 'admin' }

Sistemas de caché con almacenamiento web

Implementar un sistema de caché es otro patrón común que mejora el rendimiento de aplicaciones que realizan operaciones costosas o peticiones a APIs:

class CacheService {
  constructor(defaultExpiration = 3600000) { // 1 hora por defecto
    this.storage = new StorageManager('local');
    this.defaultExpiration = defaultExpiration;
  }

  async fetch(url, options = {}) {
    const cacheKey = `cache_${this.hashUrl(url)}`;
    const cachedResponse = this.storage.get(cacheKey);
    
    if (cachedResponse) {
      return cachedResponse;
    }
    
    try {
      const response = await fetch(url, options);
      const data = await response.json();
      
      // Guardar en caché
      this.storage.set(
        cacheKey, 
        data, 
        options.expiration || this.defaultExpiration
      );
      
      return data;
    } catch (error) {
      console.error('Error fetching data:', error);
      throw error;
    }
  }
  
  invalidate(url) {
    const cacheKey = `cache_${this.hashUrl(url)}`;
    this.storage.remove(cacheKey);
  }
  
  hashUrl(url) {
    // Implementación simple de hash para URLs
    let hash = 0;
    for (let i = 0; i < url.length; i++) {
      hash = ((hash << 5) - hash) + url.charCodeAt(i);
      hash |= 0; // Convertir a entero de 32 bits
    }
    return hash.toString(16);
  }
}

Este sistema de caché ofrece beneficios significativos:

  • Reducción de peticiones a servidores externos
  • Mejora de rendimiento para operaciones repetitivas
  • Funcionamiento offline para datos previamente cargados
  • Control granular sobre la invalidación de caché

Ejemplo de uso del sistema de caché:

const apiCache = new CacheService();

// Componente que muestra productos
async function loadProducts() {
  try {
    // Los datos se cargarán desde la caché si están disponibles
    const products = await apiCache.fetch('https://api.example.com/products');
    renderProductList(products);
  } catch (error) {
    showErrorMessage('No se pudieron cargar los productos');
  }
}

// Invalidar caché cuando se actualiza un producto
function updateProduct(productId, data) {
  // Actualizar en el servidor
  fetch(`https://api.example.com/products/${productId}`, {
    method: 'PUT',
    body: JSON.stringify(data)
  }).then(() => {
    // Invalidar la caché para forzar una recarga fresca
    apiCache.invalidate('https://api.example.com/products');
  });
}

Patrón Repository con almacenamiento local

El patrón Repository proporciona una abstracción sobre el mecanismo de almacenamiento, permitiendo operaciones CRUD consistentes independientemente del origen de datos:

class UserRepository {
  constructor() {
    this.storage = new StorageManager('local');
    this.collectionKey = 'users';
  }
  
  getAll() {
    return this.storage.get(this.collectionKey) || [];
  }
  
  getById(id) {
    const users = this.getAll();
    return users.find(user => user.id === id) || null;
  }
  
  save(user) {
    const users = this.getAll();
    const existingIndex = users.findIndex(u => u.id === user.id);
    
    if (existingIndex >= 0) {
      // Actualizar usuario existente
      users[existingIndex] = { ...users[existingIndex], ...user };
    } else {
      // Crear nuevo usuario con ID generado
      users.push({ 
        ...user, 
        id: this.generateId(),
        createdAt: Date.now()
      });
    }
    
    this.storage.set(this.collectionKey, users);
    return user;
  }
  
  delete(id) {
    const users = this.getAll();
    const filteredUsers = users.filter(user => user.id !== id);
    
    if (filteredUsers.length !== users.length) {
      this.storage.set(this.collectionKey, filteredUsers);
      return true;
    }
    
    return false;
  }
  
  generateId() {
    return Date.now().toString(36) + Math.random().toString(36).substr(2);
  }
}

Este patrón facilita la gestión de colecciones de datos y proporciona una interfaz uniforme para operaciones CRUD:

const userRepo = new UserRepository();

// Crear usuario
const newUser = userRepo.save({ name: 'Elena', email: 'elena@example.com' });

// Obtener todos los usuarios
const allUsers = userRepo.getAll();

// Actualizar usuario
userRepo.save({ id: newUser.id, role: 'editor' });

// Eliminar usuario
userRepo.delete(newUser.id);

La implementación de estos patrones de diseño mejora significativamente la arquitectura de aplicaciones que utilizan almacenamiento web, facilitando el mantenimiento, las pruebas y la escalabilidad del código. Además, proporcionan una capa de abstracción que permite cambiar la implementación subyacente sin afectar al código cliente.

Sincronización entre pestañas: Detección de cambios con el evento storage

Cuando trabajamos con aplicaciones web modernas, es común que los usuarios tengan múltiples pestañas abiertas de la misma aplicación. En estos escenarios, mantener la sincronización de datos entre estas pestañas se vuelve crucial para garantizar una experiencia coherente. JavaScript proporciona un mecanismo elegante para detectar cambios en el almacenamiento web a través del evento storage.

El evento storage se dispara automáticamente en todas las pestañas o ventanas (excepto en la que realizó el cambio) cuando se modifica el localStorage. Esta característica permite implementar sistemas de comunicación entre diferentes instancias de nuestra aplicación sin necesidad de servidores intermedios.

// Escuchar cambios en localStorage desde otras pestañas
window.addEventListener('storage', (event) => {
  console.log('Cambio detectado en otra pestaña:');
  console.log('Clave modificada:', event.key);
  console.log('Valor anterior:', event.oldValue);
  console.log('Nuevo valor:', event.oldValue);
  console.log('URL de la pestaña que realizó el cambio:', event.url);
});

El objeto event proporciona información detallada sobre el cambio realizado:

  • key: La clave que se modificó
  • oldValue: El valor anterior (antes del cambio)
  • newValue: El nuevo valor establecido
  • url: La URL de la página donde se realizó el cambio
  • storageArea: Referencia al objeto de almacenamiento (localStorage o sessionStorage)

Implementación de un sistema de notificaciones entre pestañas

Podemos aprovechar este mecanismo para crear un sistema de notificaciones que mantenga sincronizadas todas las instancias de nuestra aplicación:

class TabSynchronizer {
  constructor(channelName = 'app-sync') {
    this.channel = channelName;
    this.listeners = new Map();
    
    // Escuchar eventos de otras pestañas
    window.addEventListener('storage', (event) => {
      if (event.key === this.channel) {
        try {
          const data = JSON.parse(event.newValue);
          if (data && data.type && this.listeners.has(data.type)) {
            this.listeners.get(data.type).forEach(callback => {
              callback(data.payload);
            });
          }
        } catch (error) {
          console.error('Error al procesar mensaje entre pestañas:', error);
        }
      }
    });
  }
  
  // Enviar mensaje a otras pestañas
  broadcast(type, payload) {
    const message = JSON.stringify({
      type,
      payload,
      timestamp: Date.now()
    });
    
    localStorage.setItem(this.channel, message);
    // Necesario para que otras pestañas detecten cambios consecutivos
    // con el mismo tipo de mensaje
    setTimeout(() => localStorage.removeItem(this.channel), 100);
  }
  
  // Suscribirse a un tipo de mensaje
  subscribe(type, callback) {
    if (!this.listeners.has(type)) {
      this.listeners.set(type, new Set());
    }
    this.listeners.get(type).add(callback);
    
    // Devolver función para cancelar suscripción
    return () => {
      const callbacks = this.listeners.get(type);
      callbacks.delete(callback);
      if (callbacks.size === 0) {
        this.listeners.delete(type);
      }
    };
  }
}

Este sistema permite implementar comunicación bidireccional entre pestañas de forma sencilla:

// Crear instancia del sincronizador
const tabSync = new TabSynchronizer();

// Suscribirse a actualizaciones de usuario
const unsubscribe = tabSync.subscribe('user-updated', (userData) => {
  console.log('Datos de usuario actualizados en otra pestaña:', userData);
  updateUserInterface(userData);
});

// Cuando el usuario actualiza su perfil
function updateUserProfile(newData) {
  // Actualizar en el servidor
  api.updateProfile(newData)
    .then(response => {
      // Guardar localmente
      localStorage.setItem('user', JSON.stringify(response.data));
      
      // Notificar a otras pestañas
      tabSync.broadcast('user-updated', response.data);
      
      showSuccessMessage('Perfil actualizado correctamente');
    });
}

Sincronización de estado de autenticación

Un caso de uso común es mantener sincronizado el estado de autenticación entre pestañas. Cuando un usuario cierra sesión en una pestaña, probablemente desee que todas las demás pestañas también reflejen este cambio:

class AuthSynchronizer {
  constructor() {
    this.tabSync = new TabSynchronizer('auth-channel');
    
    // Escuchar eventos de autenticación
    this.tabSync.subscribe('auth-changed', (authState) => {
      if (authState.status === 'logged-out') {
        // Limpiar datos locales
        localStorage.removeItem('authToken');
        localStorage.removeItem('userData');
        
        // Redirigir a página de login
        window.location.href = '/login?reason=session_ended';
      }
    });
  }
  
  // Llamar cuando el usuario cierra sesión manualmente
  logout() {
    // Limpiar datos de autenticación
    localStorage.removeItem('authToken');
    localStorage.removeItem('userData');
    
    // Notificar a otras pestañas
    this.tabSync.broadcast('auth-changed', {
      status: 'logged-out',
      timestamp: Date.now()
    });
    
    // Redirigir a login
    window.location.href = '/login';
  }
}

// Uso
const authSync = new AuthSynchronizer();
document.getElementById('logout-button').addEventListener('click', () => {
  authSync.logout();
});

Sincronización de carrito de compras

Otro escenario común es la sincronización de un carrito de compras entre diferentes pestañas de una tienda online:

class CartSynchronizer {
  constructor() {
    this.tabSync = new TabSynchronizer('cart-channel');
    
    // Inicializar carrito desde localStorage
    this.cart = JSON.parse(localStorage.getItem('cart')) || { items: [], total: 0 };
    
    // Escuchar cambios en el carrito desde otras pestañas
    this.tabSync.subscribe('cart-updated', (newCart) => {
      this.cart = newCart;
      this.updateCartUI();
    });
  }
  
  // Añadir producto al carrito
  addToCart(product, quantity = 1) {
    // Buscar si el producto ya existe en el carrito
    const existingItem = this.cart.items.find(item => item.id === product.id);
    
    if (existingItem) {
      existingItem.quantity += quantity;
    } else {
      this.cart.items.push({
        id: product.id,
        name: product.name,
        price: product.price,
        quantity
      });
    }
    
    // Recalcular total
    this.cart.total = this.cart.items.reduce(
      (sum, item) => sum + (item.price * item.quantity), 0
    );
    
    // Guardar en localStorage
    localStorage.setItem('cart', JSON.stringify(this.cart));
    
    // Notificar a otras pestañas
    this.tabSync.broadcast('cart-updated', this.cart);
    
    // Actualizar interfaz
    this.updateCartUI();
  }
  
  updateCartUI() {
    // Actualizar contador de items
    document.querySelector('.cart-count').textContent = 
      this.cart.items.reduce((sum, item) => sum + item.quantity, 0);
    
    // Actualizar total
    document.querySelector('.cart-total').textContent = 
      `$${this.cart.total.toFixed(2)}`;
      
    // Actualizar lista de productos si está visible
    const cartList = document.querySelector('.cart-items');
    if (cartList) {
      // Implementación de actualización del DOM
    }
  }
}

Consideraciones y limitaciones

Al implementar sistemas de sincronización entre pestañas, es importante tener en cuenta algunas limitaciones:

  • El evento storage no se dispara en la misma pestaña que realizó el cambio
  • Existe un límite de tamaño para los datos que podemos almacenar
  • La comunicación es asíncrona pero no garantiza entrega inmediata
  • No funciona entre dominios diferentes debido a la política de mismo origen

Para casos más complejos donde estas limitaciones sean problemáticas, podemos considerar alternativas como WebSockets o la API BroadcastChannel:

// Alternativa usando BroadcastChannel (no soportada en todos los navegadores)
class ModernTabSynchronizer {
  constructor(channelName) {
    this.channel = new BroadcastChannel(channelName);
    this.listeners = new Map();
    
    this.channel.addEventListener('message', (event) => {
      const { type, payload } = event.data;
      if (this.listeners.has(type)) {
        this.listeners.get(type).forEach(callback => callback(payload));
      }
    });
  }
  
  broadcast(type, payload) {
    this.channel.postMessage({ type, payload, timestamp: Date.now() });
  }
  
  subscribe(type, callback) {
    if (!this.listeners.has(type)) {
      this.listeners.set(type, new Set());
    }
    this.listeners.get(type).add(callback);
    
    return () => {
      const callbacks = this.listeners.get(type);
      callbacks.delete(callback);
      if (callbacks.size === 0) {
        this.listeners.delete(type);
      }
    };
  }
}

La implementación de sistemas de sincronización entre pestañas mejora significativamente la experiencia de usuario en aplicaciones web modernas, garantizando coherencia de datos y reduciendo la confusión que puede surgir cuando diferentes instancias de la misma aplicación muestran estados diferentes.

CONSTRUYE TU CARRERA EN IA Y PROGRAMACIÓN SOFTWARE

Accede a +1000 lecciones y cursos con certificado. Mejora tu portfolio con certificados de superación para tu CV.

Plan mensual

19.00 € /mes

Precio normal mensual: 19 €
47 % DE DESCUENTO

Plan anual

10.00 € /mes

Ahorras 108 € al año
Precio normal anual: 120 €
Aprende JavaScript online

Ejercicios de esta lección LocalStorage y SessionStorage

Evalúa tus conocimientos de esta lección LocalStorage y SessionStorage con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.

Array

JavaScript
Puzzle

Modificación de elementos DOM

JavaScript
Proyecto

Encapsulación

JavaScript
Puzzle

Manipulación DOM

JavaScript
Proyecto

Clases y objetos

JavaScript
Código

Uso de operadores

JavaScript
Puzzle

Uso de operadores

JavaScript
Test

Estructuras de control

JavaScript
Test

Funciones

JavaScript
Código

Excepciones

JavaScript
Test

Transformación con map()

JavaScript
Código

Arrays y Métodos

JavaScript
Código

Transformación con map()

JavaScript
Puzzle

Funciones flecha

JavaScript
Test

Async / Await

JavaScript
Código

Polimorfismo

JavaScript
Código

Variables

JavaScript
Código

Selección de elementos DOM

JavaScript
Puzzle

API Fetch

JavaScript
Código

Encapsulación

JavaScript
Test

Mapas con Map

JavaScript
Código

Creación y uso de variables

JavaScript
Puzzle

Polimorfismo

JavaScript
Puzzle

Tipos de datos

JavaScript
Puzzle

Promises

JavaScript
Código

Estructuras de control

JavaScript
Puzzle

Pruebas unitarias

JavaScript
Proyecto

Encapsulación

JavaScript
Código

Inmutabilidad y programación funcional pura

JavaScript
Código

Destructuring de objetos y arrays

JavaScript
Código

Mapas con Map

JavaScript
Código

Funciones flecha

JavaScript
Puzzle

Polimorfismo

JavaScript
Test

Herencia

JavaScript
Código

Array

JavaScript
Código

Transformación con map()

JavaScript
Test

Gestor de tareas con JavaScript

JavaScript
Proyecto

Manipulación DOM

JavaScript
Test

Funciones

JavaScript
Test

Operadores avanzados

JavaScript
Código

Conjuntos con Set

JavaScript
Código

Funciones flecha

JavaScript
Código

Async / Await

JavaScript
Código

Clases y objetos

JavaScript
Código

Métodos de Strings

JavaScript
Código

Creación y uso de variables

JavaScript
Test

Excepciones

JavaScript
Puzzle

Promises

JavaScript
Código

Funciones cierre (closure)

JavaScript
Test

Funciones cierre (closure)

JavaScript
Código

Herencia

JavaScript
Puzzle

Prototipos y cadena de prototipos

JavaScript
Código

Herencia

JavaScript
Test

Estructuras de control

JavaScript
Código

Selección de elementos DOM

JavaScript
Test

Modificación de elementos DOM

JavaScript
Test

Funciones flecha

JavaScript
Código

Filtrado con filter() y find()

JavaScript
Test

Funciones cierre (closure)

JavaScript
Puzzle

Callbacks

JavaScript
Código

Funciones

JavaScript
Puzzle

Mapas con Map

JavaScript
Test

Reducción con reduce()

JavaScript
Test

Callbacks

JavaScript
Puzzle

Manipulación DOM

JavaScript
Puzzle

Introducción al DOM

JavaScript
Proyecto

Expresiones regulares

JavaScript
Código

Promises

JavaScript
Test

Async / Await

JavaScript
Test

Eventos del DOM

JavaScript
Puzzle

Introducción a JavaScript

JavaScript
Puzzle

Async / Await

JavaScript
Puzzle

Excepciones

JavaScript
Código

Promises

JavaScript
Puzzle

Selección de elementos DOM

JavaScript
Proyecto

Filtrado con filter() y find()

JavaScript
Código

Callbacks

JavaScript
Test

Eventos del DOM

JavaScript
Proyecto

Creación de clases y objetos Restaurante

JavaScript
Código

Reducción con reduce()

JavaScript
Código

Filtrado con filter() y find()

JavaScript
Puzzle

Reducción con reduce()

JavaScript
Puzzle

Conjuntos con Set

JavaScript
Puzzle

Herencia de clases

JavaScript
Código

Eventos del DOM

JavaScript
Test

Clases y objetos

JavaScript
Puzzle

Modificación de elementos DOM

JavaScript
Puzzle

Mapas con Map

JavaScript
Puzzle

Proyecto carrito compra agoodshop

JavaScript
Proyecto

Introducción a JavaScript

JavaScript
Test

Filtrado con filter() y find()

JavaScript
Código

Estructuras de control

JavaScript
Código

Funciones

JavaScript
Código

Reducción con reduce()

JavaScript
Código

Proyecto administrador de contactos

JavaScript
Proyecto

Tipos de datos

JavaScript
Test

Clases y objetos

JavaScript
Test

Array

JavaScript
Test

Conjuntos con Set

JavaScript
Test

Todas las lecciones de JavaScript

Accede a todas las lecciones de JavaScript y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.

Introducción A Javascript

JavaScript

Introducción Y Entorno

Introducción A Javascript

JavaScript

Introducción Y Entorno

Introducción A Javascript

JavaScript

Introducción Y Entorno

Tipos De Datos

JavaScript

Sintaxis

Variables

JavaScript

Sintaxis

Operadores

JavaScript

Sintaxis

Estructuras De Control

JavaScript

Sintaxis

Funciones

JavaScript

Sintaxis

Funciones Cierre (Closure)

JavaScript

Sintaxis

Métodos De Strings

JavaScript

Sintaxis

Funciones Cierre (Closure)

JavaScript

Sintaxis

Operadores Avanzados

JavaScript

Sintaxis

Funciones

JavaScript

Sintaxis

Expresiones Regulares

JavaScript

Sintaxis

Estructuras De Control

JavaScript

Sintaxis

Variables

JavaScript

Sintaxis

Arrays Y Métodos

JavaScript

Estructuras De Datos

Conjuntos Con Set

JavaScript

Estructuras De Datos

Mapas Con Map

JavaScript

Estructuras De Datos

Conjuntos Con Set

JavaScript

Estructuras De Datos

Funciones Flecha

JavaScript

Programación Funcional

Filtrado Con Filter() Y Find()

JavaScript

Programación Funcional

Transformación Con Map()

JavaScript

Programación Funcional

Reducción Con Reduce()

JavaScript

Programación Funcional

Funciones Flecha

JavaScript

Programación Funcional

Reducción Con Reduce()

JavaScript

Programación Funcional

Filtrado Con Filter() Y Find()

JavaScript

Programación Funcional

Transformación Con Map()

JavaScript

Programación Funcional

Inmutabilidad Y Programación Funcional Pura

JavaScript

Programación Funcional

Clases Y Objetos

JavaScript

Programación Orientada A Objetos

Excepciones

JavaScript

Programación Orientada A Objetos

Encapsulación

JavaScript

Programación Orientada A Objetos

Herencia

JavaScript

Programación Orientada A Objetos

Polimorfismo

JavaScript

Programación Orientada A Objetos

Excepciones

JavaScript

Programación Orientada A Objetos

Encapsulación

JavaScript

Programación Orientada A Objetos

Polimorfismo

JavaScript

Programación Orientada A Objetos

Herencia

JavaScript

Programación Orientada A Objetos

This Y Contexto

JavaScript

Programación Orientada A Objetos

Patrón De Módulos Y Namespace

JavaScript

Programación Orientada A Objetos

Clases Y Objetos

JavaScript

Programación Orientada A Objetos

Excepciones

JavaScript

Programación Orientada A Objetos

Prototipos Y Cadena De Prototipos

JavaScript

Programación Orientada A Objetos

Destructuring De Objetos Y Arrays

JavaScript

Programación Orientada A Objetos

Manipulación Dom

JavaScript

Dom

Selección De Elementos Dom

JavaScript

Dom

Modificación De Elementos Dom

JavaScript

Dom

Eventos Del Dom

JavaScript

Dom

Modificación De Elementos Dom

JavaScript

Dom

Eventos Del Dom

JavaScript

Dom

Localstorage Y Sessionstorage

JavaScript

Dom

Bom (Browser Object Model)

JavaScript

Dom

Modificación De Elementos Dom

JavaScript

Dom

Selección De Elementos Dom

JavaScript

Dom

Callbacks

JavaScript

Programación Asíncrona

Promises

JavaScript

Programación Asíncrona

Async / Await

JavaScript

Programación Asíncrona

Promises

JavaScript

Programación Asíncrona

Async / Await

JavaScript

Programación Asíncrona

Naturaleza De Js Y Event Loop

JavaScript

Programación Asíncrona

Callbacks

JavaScript

Programación Asíncrona

Websockets

JavaScript

Programación Asíncrona

Módulos En Es6

JavaScript

Construcción

Configuración De Bundlers Como Vite

JavaScript

Construcción

Eslint Y Calidad De Código

JavaScript

Construcción

Npm Y Dependencias

JavaScript

Construcción

Introducción A Pruebas En Js

JavaScript

Testing

Pruebas Unitarias

JavaScript

Testing

Accede GRATIS a JavaScript y certifícate

En esta lección

Objetivos de aprendizaje de esta lección

  1. Entender las limitaciones técnicas de localStorage y sessionStorage.
  2. Implementar medidas de seguridad para proteger datos almacenados.
  3. Desarrollar patrones de diseño para encapsular almacenamiento web.
  4. Crear sistemas de sincronización entre pestañas utilizando el evento 'storage'.
  5. Mejorar la experiencia de usuario con sistemas de caché y sincronización de estados.