Los objetos de almacenaje web localStorage y sessionStorage permiten guardar pares de clave/valor en el navegador.
Lo que es interesante sobre ellos es que los datos sobreviven a una recarga de página (en el caso de sessionStorage) y hasta un reinicio completo de navegador (en el caso de localStorage). Lo veremos en breve.
Ya tenemos cookies. ¿Por qué tener objetos adicionales?
- Al contrario que las cookies, los objetos de almacenaje web no se envÃan al servidor en cada petición. Debido a esto, podemos almacenar mucha más información. La mayorÃa de los navegadores modernos permiten almacenar, como mÃnimo, 5 megabytes de datos y tienen opciones para configurar estos lÃmites.
- También diferente de las cookies es que el servidor no puede manipular los objetos de almacenaje via cabeceras HTTP, todo se hace via JavaScript.
- El almacenaje está vinculado al orÃgen (al triplete dominio/protocolo/puerto). Esto significa que distintos protocolos o subdominios tienen distintos objetos de almacenaje, no pueden acceder a otros datos que no sean los suyos.
Ambos objetos de almacenaje proveen los mismos métodos y propiedades:
setItem(clave, valor)â almacenar un par clave/valor.getItem(clave)â obtener el valor por medio de la clave.removeItem(clave)â eliminar la clave y su valor.clear()â borrar todo.key(Ãndice)â obtener la clave de una posición dada.lengthâ el número de Ãtems almacenados.
Como puedes ver, es como una colección Map (setItem/getItem/removeItem), pero también permite el acceso a través de index con key(index).
Vamos a ver cómo funciona.
Demo de localStorage
Las principales funcionalidades de localStorage son:
- Es compartido entre todas las pestañas y ventanas del mismo origen.
- Los datos no expiran. Persisten a los reinicios de navegador y hasta del sistema operativo.
Por ejemplo, si ejecutas éste códigoâ¦
localStorage.setItem('test', 1);
⦠y cierras/abres el navegador, o simplemente abres la misma página en otra ventana, puedes coger el Ãtem que hemos guardado de este modo:
alert( localStorage.getItem('test') ); // 1
Solo tenemos que estar en el mismo dominio/puerto/protocolo, la url puede ser distinta.
localStorage es compartido por toda las ventanas del mismo origen, de modo que si guardamos datos en una ventana, el cambio es visible en la otra.
Acceso tipo Objeto
También podemos utilizar un modo de acceder/guardar claves del mismo modo que se hace con objetos, asÃ:
// guarda una clave
localStorage.test = 2;
// coge una clave
alert( localStorage.test ); // 2
// borra una clave
delete localStorage.test;
Esto se permite por razones históricas, y principalmente funciona, pero en general no se recomienda por dos motivos:
-
Si la clave es generada por el usuario, puede ser cualquier cosa, como
lengthotoString, u otro método propio delocalStorage. En este casogetItem/setItemfuncionan correctamente, pero el acceso de simil-objeto falla;let key = 'length'; localStorage[key] = 5; // Error, no se puede asignar 'length' -
Existe un evento
storage, que se dispara cuando modificamos los datos. Este evento no se dispara si utilizamos el acceso tipo objeto. Lo veremos más tarde en este capÃtulo.
Iterando sobre las claves
Los métodos proporcionan la funcionalidad get / set / remove. ¿Pero cómo conseguimos todas las claves o valores guardados?
Desafortunadamente, los objetos de almacenaje no son iterables.
Una opción es utilizar iteración sobre un array:
for(let i=0; i<localStorage.length; i++) {
let key = localStorage.key(i);
alert(`${key}: ${localStorage.getItem(key)}`);
}
Otra opción es utilizar el loop especÃfico para objetos for key in localStorage tal como hacemos en objetos comunes.
Esta opción itera sobre las claves, pero también devuelve campos propios de localStorage que no necesitamos:
// mal intento
for(let key in localStorage) {
alert(key); // muestra getItem, setItem y otros campos que no nos interesan
}
⦠De modo que necesitamos o bien filtrar campos des del prototipo con la validación hasOwnProperty:
for(let key in localStorage) {
if (!localStorage.hasOwnProperty(key)) {
continue; // se salta claves como "setItem", "getItem" etc
}
alert(`${key}: ${localStorage.getItem(key)}`);
}
⦠O simplemente acceder a las claves âpropiasâ con Object.keys y iterar sobre éstas si es necesario:
let keys = Object.keys(localStorage);
for(let key of keys) {
alert(`${key}: ${localStorage.getItem(key)}`);
}
Esta última opción funciona, ya que Object.keys solo devuelve las claves que pertenecen al objeto, ignorando el prototipo.
Solo strings
Hay que tener en cuenta que tanto la clave como el valor deben ser strings.
Si fueran de cualquier otro tipo, como un número o un objeto, se convertirÃan a cadena de texto automáticamente:
localStorage.user = {name: "John"};
alert(localStorage.user); // [object Object]
A pesar de eso, podemos utilizar JSON para almacenar objetos:
localStorage.user = JSON.stringify({name: "John"});
// en algún momento más tarde
let user = JSON.parse( localStorage.user );
alert( user.name ); // John
También es posible pasar a texto todo el objeto de almacenaje, por ejemplo para debugear:
// se ha añadido opciones de formato a JSON.stringify para que el objeto se lea mejor
alert( JSON.stringify(localStorage, null, 2) );
sessionStorage
El objeto sessionStorage se utiliza mucho menos que localStorage.
Las propiedades y métodos son los mismos, pero es mucho más limitado:
sessionStoragesolo existe dentro de la pestaña actual del navegador.- Otra pestaña con la misma página tendrá un almacenaje distinto.
- Pero se comparte entre iframes en la pestaña (asumiendo que tengan el mismo orÃgen).
- Los datos sobreviven un refresco de página, pero no cerrar/abrir la pestaña.
Vamos a verlo en acción.
Ejecuta éste códigoâ¦
sessionStorage.setItem('test', 1);
⦠Y recarga la página. Aún puedes acceder a los datos:
alert( sessionStorage.getItem('test') ); // después de la recarga: 1
⦠Pero si abres la misma página en otra pestaña, y lo intentas de nuevo, el código anterior devuelve null, que significa que no se ha encontrado nada.
Esto es exactamente porque sessionStorage no está vinculado solamente al orÃgen, sino también a la pestaña del navegador. Por ésta razón sessionStorage se usa relativamente poco.
Evento storage
Cuando los datos se actualizan en localStorage o en sessionStorage, se dispara el evento storage con las propiedades:
keyâ la clave que ha cambiado, (nullsi se llama.clear()).oldValueâ el anterior valor (nullsi se añade una clave).newValueâ el nuevo valor (nullsi se borra una clave).urlâ la url del documento donde ha pasado la actualización.storageAreaâ bien el objetolocalStorageosessionStorage, donde se ha producido la actualización.
El hecho importante es: el evento se dispara en todos los objetos window donde el almacenaje es accesible, excepto en el que lo ha causado.
Vamos a desarrollarlo.
Imagina que tienes dos ventanas con el mismo sitio en cada una, de modo que localStorage es compartido entre ellas.
Quizá quieras abrir ésta página en dos ventanas distintas para probar el código que sigue.
Si ambas ventanas están escuchando el evento window.onstorage, cada una reaccionará a las actualizaciones que pasen en la otra.
// se dispara en actualizaciones hechas en el mismo almacenaje, desde otros documentos
window.onstorage = event => { // también puede usar window.addEventListener('storage', event => {
if (event.key != 'now') return;
alert(event.key + ':' + event.newValue + " at " + event.url);
};
localStorage.setItem('now', Date.now());
Hay que tener en cuenta que el evento también contiene: event.url â la url del documento en que se actualizaron los datos.
También que event.storageArea contiene el objeto de almacenaje â el evento es el mismo para sessionStorage y localStorage --, de modo que storageArea referencia el que se modificó. Podemos hasta querer cambiar datos en él, para âresponderâ a un cambio.
Esto permite que distintas ventanas del mismo orÃgen puedan intercambiar mensajes.
Los navegadores modernos también soportan la API de Broadcast channel API, la API especÃfica para la comunicación entre ventanas del mismo orÃgen. Es más completa, pero tiene menos soporte. Hay librerÃas que añaden polyfills para ésta API basados en localStorage para que se pueda utilizar en cualquier entorno.
Resumen
Los objetos de almacenaje web localStorage y sessionStorage permiten guardar pares de clave/valor en el navegador.
- Tanto la
clavecomo elvalordeben ser strings. - El lÃmite es de más de 5mb+, dependiendo del navegador.
- No expiran.
- Los datos están vinculados al origen (dominio/puerto/protocolo).
localStorage |
sessionStorage |
|---|---|
| Compartida entre todas las pestañas y ventanas que tengan el mismo orÃgen | Accesible en una pestaña del navegador, incluyendo iframes del mismo origen |
| Sobrevive a reinicios del navegador | Muere al cerrar la pestaña |
API:
setItem(clave, valor)â guarda pares clave/valor.getItem(clave)â coge el valor de una clave.removeItem(clave)â borra una clave con su valor.clear()â borra todo.key(Ãndice)â coge la clave en una posición determinada.lengthâ el número de Ãtems almacenados.- Utiliza
Object.keyspara conseguir todas las claves. - Puede utilizar las claves como propiedades de objetor, pero en ese caso el evento
storageno se dispara
Evento storage:
- Se dispara en las llamadas a
setItem,removeItem,clear. - Contiene todos los datos relativos a la operación (
key/oldValue/newValue), laurldel documento y el objeto de almacenaje. - Se dispara en todos los objetos
windowque tienen acceso al almacenaje excepto el que ha generado el evento (en una pestaña en el caso desessionStorageo globalmente en el caso delocalStorage).
Comentarios
<code>, para varias lÃneas â envolverlas en la etiqueta<pre>, para más de 10 lÃneas â utilice una entorno controlado (sandbox) (plnkr, jsbin, codepenâ¦)