Me gustaría almacenar un objeto JavaScript en HTML5 localStorage
, pero mi objeto aparentemente se está convirtiendo en una cadena.
Puedo almacenar y recuperar tipos y matrices de JavaScript primitivos usando localStorage
, pero los objetos no parecen funcionar. ¿Deberían ellos?
Aquí está mi código:
var testObject = { 'one': 1, 'two': 2, 'three': 3 };
console.log('typeof testObject: ' + typeof testObject);
console.log('testObject properties:');
for (var prop in testObject) {
console.log(' ' + prop + ': ' + testObject[prop]);
}
// Put the object into storage
localStorage.setItem('testObject', testObject);
// Retrieve the object from storage
var retrievedObject = localStorage.getItem('testObject');
console.log('typeof retrievedObject: ' + typeof retrievedObject);
console.log('Value of retrievedObject: ' + retrievedObject);
La salida de la consola es
typeof testObject: object
testObject properties:
one: 1
two: 2
three: 3
typeof retrievedObject: string
Value of retrievedObject: [object Object]
Me parece como el setItem
método está convirtiendo la entrada en una cadena antes de almacenarla.
Veo este comportamiento en Safari, Chrome y Firefox, así que supongo que es mi malentendido de las especificaciones de HTML5 Web Storage , no un error o limitación específica del navegador.
Intenté darle sentido al algoritmo de clonación estructurado descrito en http://www.w3.org/TR/html5/infrastructure.html . No entiendo completamente lo que dice, pero tal vez mi problema tiene que ver con que las propiedades de mi objeto no son enumerables (???)
¿Hay una solución fácil?
Actualización: El W3C finalmente cambió de opinión sobre la especificación de clones estructurados y decidió cambiar la especificación para que coincida con las implementaciones. Ver https://www.w3.org/Bugs/Public/show_bug.cgi?id=12111 . Entonces esta pregunta ya no es 100% válida, pero las respuestas aún pueden ser de interés.
fuente
Respuestas:
Mirando a Apple , Mozilla y Mozilla nuevamente , la funcionalidad parece estar limitada para manejar solo pares de clave / valor de cadena.
Una solución alternativa puede ser encadenar su objeto antes de almacenarlo y luego analizarlo cuando lo recupere:
fuente
JSON.stringify()
expande el objeto referenciado a su "contenido" completo (implícitamente encadenado) en el objeto que encadenamos. Ver: stackoverflow.com/a/12659424/2044940Una mejora menor en una variante :
Debido a la evaluación de cortocircuito ,
getObject()
será inmediatamente volvernull
sikey
no está en el almacenamiento. Tampoco arrojará unaSyntaxError
excepción sivalue
es""
(la cadena vacía;JSON.parse()
no puede manejar eso).fuente
var userObject = { userId: 24, name: 'Jack Bauer' };
y configurarlolocalStorage.setObject('user', userObject);
Luego recuperarlo del almacenamientouserObject = localStorage.getObject('user');
Incluso puede almacenar una matriz de objetos si lo desea.key
no está en el almacenamiento local,window.localStorage.getItem(key)
los retornosnull
- lo hace no lanzan una excepción "Acceso ilegal" - yJSON.parse(null)
vuelvenull
así - no no una excepción tampoco, ni en cromo 21 ni por ES 5.1 sección 15.12.2 , yaString(null) === "null"
que puede ser interpretado como un literal JSON .""
(la cadena vacía) antes. Debido a que se convierte de tipo afalse
yJSON.parse("")
, lo que arrojaría unaSyntaxError
excepción, no se llama.Puede resultarle útil ampliar el objeto Almacenamiento con estos métodos útiles:
De esta forma obtienes la funcionalidad que realmente querías, aunque debajo de la API solo se admiten cadenas.
fuente
localStorage.setObject
.getObject()
arrojará unaSyntaxError
excepción si el valor almacenado es""
, porqueJSON.parse()
no puede manejar eso. Vea mi edición de la respuesta de Guria para más detalles.Extender el objeto Storage es una solución increíble. Para mi API, he creado una fachada para localStorage y luego verifico si es un objeto o no mientras lo configuro y lo obtengo.
fuente
data.set('username': 'ifedi', 'fullname': { firstname: 'Ifedi', lastname: 'Okonkwo'});
:?Stringify no resuelve todos los problemas
Parece que las respuestas aquí no cubren todos los tipos que son posibles en JavaScript, así que aquí hay algunos ejemplos breves sobre cómo tratarlos correctamente:
No recomiendo almacenar funciones porque
eval()
es malo puede provocar problemas de seguridad, optimización y depuración. En general,eval()
nunca debe usarse en código JavaScript.Miembros privados
El problema con el uso
JSON.stringify()
para almacenar objetos es que esta función no puede serializar miembros privados. Este problema se puede resolver sobrescribiendo el.toString()
método (que se llama implícitamente al almacenar datos en el almacenamiento web):Referencias circulares
Otro problema
stringify
que no puede resolver son las referencias circulares:En este ejemplo,
JSON.stringify()
arrojará unTypeError
"Conversión de estructura circular a JSON" . Si se debe almacenar referencias circulares, se podría usar el segundo parámetro deJSON.stringify()
:Sin embargo, encontrar una solución eficiente para almacenar referencias circulares depende en gran medida de las tareas que deben resolverse, y restaurar dichos datos tampoco es trivial.
Ya hay alguna pregunta sobre SO que trata este problema: Stringify (convertir a JSON) un objeto JavaScript con referencia circular
fuente
Hay una gran biblioteca que envuelve muchas soluciones, por lo que incluso admite navegadores antiguos llamados jStorage
Puedes establecer un objeto
Y recuperarlo fácilmente
fuente
En teoría, es posible almacenar objetos con funciones:
Sin embargo, la serialización / deserialización de funciones no es confiable porque depende de la implementación .
fuente
c.f[k] = escape(a[k]);
con Unicode-safec.f[k] = encodeURIComponent(a[k]);
yeval('b.' + k + ' = ' + unescape(data.f[k]));
conb[k] = eval("(" + decodeURIComponent(data.f[k]) + ")");
. Los paréntesis son obligatorios porque su función, si se serializa correctamente, es probable que sea anónima, lo que no es como es / es válida / Declaración / (poreval()
lo tanto ) arrojaría unaSyntaxError
excepción de lo contrario).typeof
es un operador , no lo escriba como si fuera una función. Reemplazartypeof(a[k])
contypeof a[k]
.for
-in
no fue filtrado por propiedades propias. 3. El estilo del código, incluidas las referencias, era inconsistente.the use and placement of white space, line terminators, and semicolons within the representation String is implementation-dependent.
que no veo diferencias funcionales.Note *in particular* that …
. Pero la especificación del valor de retorno comienza conAn implementation-dependent representation of the function is returned. This representation has the syntax of a FunctionDeclaration.
El valor de retorno puede serfunction foo () {}
, suponiendo una implementación conforme .Llegué a esta publicación después de tocar otra publicación que se ha cerrado como un duplicado de esta, titulada '¿cómo almacenar una matriz en el almacenamiento local?'. Lo cual está bien, excepto que ninguno de los hilos proporciona una respuesta completa sobre cómo puede mantener una matriz en localStorage; sin embargo, he logrado crear una solución basada en la información contenida en ambos hilos.
Entonces, si alguien más desea poder empujar / hacer estallar / cambiar elementos dentro de una matriz, y desea que esa matriz se almacene en localStorage o, de hecho, sessionStorage, aquí tiene:
ejemplo de uso: almacenamiento de cadenas simples en la matriz localStorage:
ejemplo de uso: almacenamiento de objetos en la matriz sessionStorage:
métodos comunes para manipular matrices:
fuente
Se recomienda utilizar una biblioteca de abstracción para muchas de las características que se analizan aquí, así como una mejor compatibilidad. Muchas opciones:
fuente
Puedes usar localDataStorage para almacenar de forma transparente los tipos de datos de JavaScript (matriz, booleano, fecha, flotante, entero, cadena y objeto). También proporciona una ofuscación de datos liviana, comprime cadenas automáticamente, facilita la consulta por clave (nombre) y la consulta por valor (clave) y ayuda a imponer el almacenamiento compartido segmentado dentro del mismo dominio mediante el prefijo de claves.
[RENUNCIA] Soy el autor de la utilidad [/ RENUNCIA]
Ejemplos:
Como puede ver, se respetan los valores primitivos.
fuente
Otra opción sería usar un complemento existente.
Por ejemplo, persisto es un proyecto de código abierto que proporciona una interfaz fácil para localStorage / sessionStorage y automatiza la persistencia de los campos de formulario (entrada, botones de opción y casillas de verificación).
(Descargo de responsabilidad: soy el autor).
fuente
localStroage
; exp:var lsh = new localStorageHelper(); lsh.setItem('bob', 'bill');
también incluye eventos.Puede usar ejson para almacenar los objetos como cadenas.
Aquí está mi contenedor localStorage usando ejson
https://github.com/UziTech/storage.js
Agregué algunos tipos a mi contenedor, incluidas las expresiones y funciones regulares
fuente
Hice otro contenedor minimalista con solo 20 líneas de código para permitir usarlo como debería:
https://github.com/zevero/simpleWebstorage
fuente
Para usuarios de Typecript que deseen establecer y obtener propiedades escritas:
Ejemplo de uso :
fuente
https://github.com/adrianmay/rhaboo es una capa de azúcar de LocalStorage que te permite escribir cosas como esta:
No utiliza JSON.stringify / parse porque eso sería inexacto y lento en objetos grandes. En cambio, cada valor de terminal tiene su propia entrada localStorage.
Probablemente puedas adivinar que podría tener algo que ver con rhaboo.
fuente
Aquí una versión extendida del código publicado por @danott
También va a poner en práctica de eliminación valor de localStorage y muestra la forma en que añade una capa getter y setter así que en vez de
localstorage.setItem(preview, true)
puedes escribir
config.preview = true
Bien, aquí estaban:
Bueno, puedes quitar la parte del alias
.bind(...)
. Sin embargo, lo acabo de poner ya que es realmente bueno saber sobre esto. Me llevó horas descubrir por qué un simpleget = localStorage.getItem;
no funcionafuente
Hice algo que no rompe los objetos de almacenamiento existentes, sino que crea un contenedor para que pueda hacer lo que quiera. El resultado es un objeto normal, sin métodos, con acceso como cualquier objeto.
Lo que hice.
Si quieres que 1
localStorage
propiedad sea mágica:Si necesitas varios:
Todo lo que hagas
prop
o los objetos dentrostorage
se guardarán automáticamentelocalStorage
. Siempre estás jugando con un objeto real, por lo que puedes hacer cosas como esta:Y cada nuevo objeto dentro de un objeto rastreado será rastreado automáticamente.
El gran inconveniente: depende, por
Object.observe()
lo que tiene un soporte de navegador muy limitado. Y no parece que vendrá para Firefox o Edge en el corto plazo.fuente
No puede almacenar el valor clave sin el formato de cadena .
LocalStorage solo admite el formato de cadena para clave / valor.
Es por eso que debe convertir sus datos en cadenas, sea lo que sea Array u Object .
Para almacenar datos en localStorage, en primer lugar, hágalo en cadena utilizando el método JSON.stringify () .
Luego, cuando desee recuperar datos, debe analizar la Cadena a Objeto nuevamente.
Espero eso ayude.
fuente
Para almacenar un objeto, puede hacer letras que pueda usar para llevar un objeto de una cadena a un objeto (puede que no tenga sentido). Por ejemplo
Esta técnica causará algunos problemas técnicos si usa la letra que usó para dividir el objeto, y también es muy experimental.
fuente
Encontré una manera de hacerlo funcionar con objetos que tienen referencias cíclicas.
Hagamos un objeto con referencias cíclicas.
No podemos hacer
JSON.stringify
aquí, debido a las referencias circulares.LOCALSTORAGE.CYCLICJSON
tiene.stringify
e.parse
igual que lo normalJSON
, pero funciona con objetos con referencias circulares. ("Works" significa parse (stringify (obj)) y obj son profundamente iguales Y tienen conjuntos idénticos de "igualdades internas")Pero solo podemos usar los atajos:
Entonces,
recovered
será "lo mismo" que obj, en el siguiente sentido:Aquí está la implementación de
LOCALSTORAGE
fuente
localStorage.setItem ('usuario', JSON.stringify (usuario));
Luego, para recuperarlo de la tienda y convertirlo en un objeto nuevamente:
usuario var = JSON.parse (localStorage.getItem ('usuario'));
Si necesitamos eliminar todas las entradas de la tienda, simplemente podemos hacer:
localStorage.clear ();
fuente