javascript - guardar - localstorage jquery
Almacenamiento de objetos en HTML5 localStorage (27)
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:
//Objects and Arrays:
var obj = {key: "value"};
localStorage.object = JSON.stringify(obj); //Will ignore private members
obj = JSON.parse(localStorage.object);
//Boolean:
var bool = false;
localStorage.bool = bool;
bool = (localStorage.bool === "true");
//Numbers:
var num = 42;
localStorage.num = num;
num = +localStorage.num; //short for "num = parseFloat(localStorage.num);"
//Dates:
var date = Date.now();
localStorage.date = date;
date = new Date(parseInt(localStorage.date));
//Regular expressions:
var regex = /^No/.[/d]*$/i; //usage example: "No.42".match(regex);
localStorage.regex = regex;
var components = localStorage.regex.match("^/(.*)/([a-z]*)$");
regex = new RegExp(components[1], components[2]);
//Functions (not recommended):
function func(){}
localStorage.func = func;
eval( localStorage.func ); //recreates the function with the name "func"
No recomiendo almacenar funciones porque eval()
es malvado, puede llevar a problemas relacionados con la seguridad, la optimización y la depuración. En general, eval()
nunca debe usarse en código JavaScript.
Miembros privados
El problema con el uso de JSON.stringify()
para almacenar objetos es que esta función no puede serializar miembros privados. Este problema se puede resolver sobrescribiendo el método .toString()
(que se llama implícitamente al almacenar datos en el almacenamiento web):
//Object with private and public members:
function MyClass(privateContent, publicContent){
var privateMember = privateContent || "defaultPrivateValue";
this.publicMember = publicContent || "defaultPublicValue";
this.toString = function(){
return ''{"private": "'' + privateMember + ''", "public": "'' + this.publicMember + ''"}'';
};
}
MyClass.fromString = function(serialisedString){
var properties = JSON.parse(serialisedString || "{}");
return new MyClass( properties.private, properties.public );
};
//Storing:
var obj = new MyClass("invisible", "visible");
localStorage.object = obj;
//Loading:
obj = MyClass.fromString(localStorage.object);
Referencias circulares
Otro problema stringify
que no se puede lidiar son las referencias circulares:
var obj = {};
obj["circular"] = obj;
localStorage.object = JSON.stringify(obj); //Fails
En este ejemplo, JSON.stringify()
lanzará un TypeError
"Convertir estructura circular a JSON" . Si se debe JSON.stringify()
almacenamiento de referencias circulares, se puede usar el segundo parámetro de JSON.stringify()
:
var obj = {id: 1, sub: {}};
obj.sub["circular"] = obj;
localStorage.object = JSON.stringify( obj, function( key, value) {
if( key == ''circular'') {
return "$ref"+value.id+"$";
} else {
return value;
}
});
Sin embargo, encontrar una solución eficiente para almacenar referencias circulares depende en gran medida de las tareas que deben resolverse, y la restauración de dichos datos tampoco es trivial.
Ya hay alguna pregunta sobre SO que trata este problema: Stringify (convertir a JSON) un objeto de JavaScript con referencia circular
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 utilizando 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 que el método setItem
está convirtiendo la entrada en una cadena antes de almacenarla.
Veo este comportamiento en Safari, Chrome y Firefox, por lo que asumo que es mi mala interpretación de la especificación de almacenamiento web HTML5 , no un error o limitación específica del navegador.
He intentado entender el algoritmo de clonación estructurado descrito en http://www.w3.org/TR/html5/infrastructure.html . No entiendo completamente lo que está diciendo, pero tal vez mi problema tenga que ver con que las propiedades de mi objeto no sean enumerables (???)
¿Hay una solución fácil?
Actualización: el W3C finalmente cambió de opinión acerca de la especificación de clonación estructurada y decidió cambiar la especificación para que coincida con las implementaciones. Consulte https://www.w3.org/Bugs/Public/show_bug.cgi?id=12111 . Entonces, esta pregunta ya no es 100% válida, pero las respuestas pueden ser interesantes.
Aquí una versión ampliada del código publicado por @danott
También implementará el valor de eliminación de localstorage y muestra cómo agregar una capa de Getter y Setter para que en lugar de
localstorage.setItem(preview, true)
puedes escribir
config.preview = true
Bien aquí vamos
var PT=Storage.prototype
if (typeof PT._setItem >=''u'') PT._setItem = PT.setItem;
PT.setItem = function(key, value)
{
if (typeof value >=''u'')//..ndefined
this.removeItem(key)
else
this._setItem(key, JSON.stringify(value));
}
if (typeof PT._getItem >=''u'') PT._getItem = PT.getItem;
PT.getItem = function(key)
{
var ItemData = this._getItem(key)
try
{
return JSON.parse(ItemData);
}
catch(e)
{
return ItemData;
}
}
// Aliases for localStorage.set/getItem
get = localStorage.getItem.bind(localStorage)
set = localStorage.setItem.bind(localStorage)
// Create ConfigWrapperObject
var config = {}
// Helper to create getter & setter
function configCreate(PropToAdd){
Object.defineProperty( config, PropToAdd, {
get: function () { return ( get(PropToAdd) ) },
set: function (val) { set(PropToAdd, val ) }
})
}
//------------------------------
// Usage Part
// Create properties
configCreate(''preview'')
configCreate(''notification'')
//...
// Config Data transfer
//set
config.preview = true
//get
config.preview
// delete
config.preview = undefined
Bueno, puedes quitar la parte de los alias con .bind(...)
. Sin embargo, solo lo puse ya que es realmente bueno saber sobre esto. Me tomé horas para descubrir por qué un simple get = localStorage.getItem;
no trabajes
En cuanto a la documentación de Apple , Mozilla y Microsoft , la funcionalidad parece estar limitada para manejar solo pares de cadena clave / valor.
Una solución puede consistir en stringify su objeto antes de almacenarlo, y luego analizarlo cuando lo recupere:
var testObject = { ''one'': 1, ''two'': 2, ''three'': 3 };
// Put the object into storage
localStorage.setItem(''testObject'', JSON.stringify(testObject));
// Retrieve the object from storage
var retrievedObject = localStorage.getItem(''testObject'');
console.log(''retrievedObject: '', JSON.parse(retrievedObject));
En teoría, es posible almacenar objetos con funciones:
function store (a)
{
var c = {f: {}, d: {}};
for (var k in a)
{
if (a.hasOwnProperty(k) && typeof a[k] === ''function'')
{
c.f[k] = encodeURIComponent(a[k]);
}
}
c.d = a;
var data = JSON.stringify(c);
window.localStorage.setItem(''CODE'', data);
}
function restore ()
{
var data = window.localStorage.getItem(''CODE'');
data = JSON.parse(data);
var b = data.d;
for (var k in data.f)
{
if (data.f.hasOwnProperty(k))
{
b[k] = eval("(" + decodeURIComponent(data.f[k]) + ")");
}
}
return b;
}
Sin embargo, la serialización / deserialización de la función no es confiable porque depende de la implementación .
Extender el objeto de almacenamiento es una solución impresionante. Para mi API, he creado una fachada para localStorage y luego verifico si es un objeto o no mientras lo configuro y obtengo.
var data = {
set: function(key, value) {
if (!key || !value) {return;}
if (typeof value === "object") {
value = JSON.stringify(value);
}
localStorage.setItem(key, value);
},
get: function(key) {
var value = localStorage.getItem(key);
if (!value) {return;}
// assume it is an object that has been stringified
if (value[0] === "{") {
value = JSON.parse(value);
}
return value;
}
}
Hay una gran biblioteca que contiene muchas soluciones, por lo que incluso admite navegadores más antiguos llamados jStorage
Puedes establecer un objeto
$.jStorage.set(key, value)
Y recuperarlo fácilmente
value = $.jStorage.get(key)
value = $.jStorage.get(key, "default value")
He modificado un poco la respuesta más votada. Soy un fan de tener una sola función en lugar de 2 si no es necesario.
Storage.prototype.object = function(key, val) {
if ( typeof val === "undefined" ) {
var value = this.getItem(key);
return value ? JSON.parse(value) : null;
} else {
this.setItem(key, JSON.stringify(val));
}
}
localStorage.object("test", {a : 1}); //set value
localStorage.object("test"); //get value
Además, si no se establece ningún valor, devuelve null
lugar de false
. false
tiene algún significado, null
no lo tiene.
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.
Si quieres que 1 propiedad localStorage
sea mágica:
var prop = ObjectStorage(localStorage, ''prop'');
Si necesitas varios:
var storage = ObjectStorage(localStorage, [''prop'', ''more'', ''props'']);
Todo lo que haga para prop
o los objetos dentro del storage
se guardará automáticamente en localStorage
. Siempre estás jugando con un objeto real, por lo que puedes hacer cosas como esta:
storage.data.list.push(''more data'');
storage.another.list.splice(1, 2, {another: ''object''});
Y cada nuevo objeto dentro de un objeto rastreado será rastreado automáticamente.
El gran inconveniente: depende de Object.observe()
por lo que tiene un soporte de navegador muy limitado. Y no parece que vendrá para Firefox o Edge en el corto plazo.
Hice otra envoltura minimalista con solo 20 líneas de código para permitir su uso como debería:
localStorage.set(''myKey'',{a:[1,2,5], b: ''ok''});
localStorage.has(''myKey''); // --> true
localStorage.get(''myKey''); // --> {a:[1,2,5], b: ''ok''}
localStorage.keys(); // --> [''myKey'']
localStorage.remove(''myKey'');
Llegué a este post después de golpear en otro post que se ha cerrado como un duplicado de este, titulado ''¿Cómo almacenar una matriz en el almacenamiento local?''. Lo cual está bien, excepto que ninguno de los hilos en realidad 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 / abrir / cambiar elementos dentro de una matriz, y quiere que esa matriz se almacene en localStorage o incluso sessionStorage, aquí tienes:
Storage.prototype.getArray = function(arrayName) {
var thisArray = [];
var fetchArrayObject = this.getItem(arrayName);
if (typeof fetchArrayObject !== ''undefined'') {
if (fetchArrayObject !== null) { thisArray = JSON.parse(fetchArrayObject); }
}
return thisArray;
}
Storage.prototype.pushArrayItem = function(arrayName,arrayItem) {
var existingArray = this.getArray(arrayName);
existingArray.push(arrayItem);
this.setItem(arrayName,JSON.stringify(existingArray));
}
Storage.prototype.popArrayItem = function(arrayName) {
var arrayItem = {};
var existingArray = this.getArray(arrayName);
if (existingArray.length > 0) {
arrayItem = existingArray.pop();
this.setItem(arrayName,JSON.stringify(existingArray));
}
return arrayItem;
}
Storage.prototype.shiftArrayItem = function(arrayName) {
var arrayItem = {};
var existingArray = this.getArray(arrayName);
if (existingArray.length > 0) {
arrayItem = existingArray.shift();
this.setItem(arrayName,JSON.stringify(existingArray));
}
return arrayItem;
}
Storage.prototype.unshiftArrayItem = function(arrayName,arrayItem) {
var existingArray = this.getArray(arrayName);
existingArray.unshift(arrayItem);
this.setItem(arrayName,JSON.stringify(existingArray));
}
Storage.prototype.deleteArray = function(arrayName) {
this.removeItem(arrayName);
}
Ejemplo de uso: almacenamiento de cadenas simples en la matriz localStorage:
localStorage.pushArrayItem(''myArray'',''item one'');
localStorage.pushArrayItem(''myArray'',''item two'');
Ejemplo de uso: almacenamiento de objetos en la matriz sessionStorage:
var item1 = {}; item1.name = ''fred''; item1.age = 48;
sessionStorage.pushArrayItem(''myArray'',item1);
var item2 = {}; item2.name = ''dave''; item2.age = 22;
sessionStorage.pushArrayItem(''myArray'',item2);
Métodos comunes para manipular matrices:
.pushArrayItem(arrayName,arrayItem); -> adds an element onto end of named array
.unshiftArrayItem(arrayName,arrayItem); -> adds an element onto front of named array
.popArrayItem(arrayName); -> removes & returns last array element
.shiftArrayItem(arrayName); -> removes & returns first array element
.getArray(arrayName); -> returns entire array
.deleteArray(arrayName); -> removes entire array from storage
Mejor que hagas funciones como definidor y obtenedor de localStorage , de esta manera tendrás un mejor control y no tendrás que repetir el análisis JSON y todo. incluso manejará su ("") clave de cadena vacía / caso de datos sin problemas.
function setItemInStorage(dataKey, data){
localStorage.setItem(dataKey, JSON.stringify(data));
}
function getItemFromStorage(dataKey){
var data = localStorage.getItem(dataKey);
return data? JSON.parse(data): null ;
}
setItemInStorage(''user'', { name:''tony stark'' });
getItemFromStorage(''user''); /* return {name:''tony stark''} */
Mejora en la respuesta de @Guria:
Storage.prototype.setObject = function (key, value) {
this.setItem(key, JSON.stringify(value));
};
Storage.prototype.getObject = function (key) {
var value = this.getItem(key);
try {
return JSON.parse(value);
}
catch(err) {
console.log("JSON parse failed for lookup of ", key, "/n error was: ", err);
return null;
}
};
Otra opción sería usar un plugin 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: yo soy el autor.)
Para almacenar un objeto, puede hacer letras que pueda usar para obtener un objeto de una cadena a un objeto (puede que no tenga sentido). Por ejemplo
var obj = {a: "lol", b: "A", c: "hello world"};
function saveObj (key){
var j = "";
for(var i in obj){
j += (i+"|"+obj[i]+"~");
}
localStorage.setItem(key, j);
} // Saving Method
function getObj (key){
var j = {};
var k = localStorage.getItem(key).split("~");
for(var l in k){
var m = k[l].split("|");
j[m[0]] = m[1];
}
return j;
}
saveObj("obj"); // undefined
getObj("obj"); // {a: "lol", b: "A", c: "hello world"}
Esta técnica causará algunas fallas si usa la letra que usó para dividir el objeto y también es muy experimental.
Para usuarios de Typescript dispuestos a establecer y obtener propiedades escritas:
/**
* Silly wrapper to be able to type the storage keys
*/
export class TypedStorage<T> {
public removeItem(key: keyof T): void {
localStorage.removeItem(key);
}
public getItem<K extends keyof T>(key: K): T[K] | null {
const data: string | null = localStorage.getItem(key);
return JSON.parse(data);
}
public setItem<K extends keyof T>(key: K, value: T[K]): void {
const data: string = JSON.stringify(value);
localStorage.setItem(key, data);
}
}
// write an interface for the storage
interface MyStore {
age: number,
name: string,
address: {city:string}
}
const storage: TypedStorage<MyStore> = new TypedStorage<MyStore>();
storage.setItem("wrong key", ""); // error unknown key
storage.setItem("age", "hello"); // error, age should be number
storage.setItem("address", {city:"Here"}); // ok
const address: {city:string} = storage.getItem("address");
Puede resultarle útil extender el objeto de almacenamiento con estos métodos prácticos:
Storage.prototype.setObject = function(key, value) {
this.setItem(key, JSON.stringify(value));
}
Storage.prototype.getObject = function(key) {
return JSON.parse(this.getItem(key));
}
De esta manera, obtiene la funcionalidad que realmente deseaba, aunque la API solo admite cadenas.
Puede usar localDataStorage para almacenar de forma transparente los tipos de datos de javascript (Array, Boolean, Date, Float, Integer, String y Object). También proporciona ofuscación de datos ligeros, comprime automáticamente cadenas, facilita la consulta por clave (nombre), así como la consulta por valor (clave), y ayuda a imponer el almacenamiento compartido segmentado dentro del mismo dominio mediante el prefijo de claves.
[DESCARGO DE RESPONSABILIDAD] Soy el autor de la utilidad [/ DESCARGO DE RESPONSABILIDAD]
Ejemplos:
localDataStorage.set( ''key1'', ''Belgian'' )
localDataStorage.set( ''key2'', 1200.0047 )
localDataStorage.set( ''key3'', true )
localDataStorage.set( ''key4'', { ''RSK'' : [1,''3'',5,''7'',9] } )
localDataStorage.set( ''key5'', null )
localDataStorage.get( ''key1'' ) --> ''Belgian''
localDataStorage.get( ''key2'' ) --> 1200.0047
localDataStorage.get( ''key3'' ) --> true
localDataStorage.get( ''key4'' ) --> Object {RSK: Array(5)}
localDataStorage.get( ''key5'' ) --> null
Como puedes ver, los valores primitivos son respetados.
Puedes usar ejson para almacenar los objetos como cadenas.
EJSON es una extensión de JSON para admitir más tipos. Es compatible con todos los tipos seguros para JSON, así como:
- Fecha (Fecha JavaScript)
- Binario (JavaScript
Uint8Array
o el resultado de EJSON.newBinary )- Tipos definidos por el usuario (vea EJSON.addType . Por ejemplo, Mongo.ObjectID se implementa de esta manera).
Todas las serializaciones EJSON también son JSON válidas. Por ejemplo, un objeto con una fecha y un búfer binario se serializaría en EJSON como:
{ "d": {"$date": 1358205756553}, "b": {"$binary": "c3VyZS4="} }
Aquí está mi envoltorio de almacenamiento local usando ejson
https://github.com/UziTech/storage.js
Agregué algunos tipos a mi envoltorio incluyendo expresiones regulares y funciones
Recomiende el uso de una biblioteca de abstracción para muchas de las características aquí discutidas, así como una mejor compatibilidad. Un montón de opciones:
- jStorage o simpleStorage << mi preferencia
- localForage
- alekseykulikov/storage
- Lawnchair
- Store.js << otra buena opción
- OMG
También puede anular los setItem(key,value)
predeterminados de setItem(key,value)
almacenamiento setItem(key,value)
y getItem(key)
para manejar objetos / arreglos como cualquier otro tipo de datos. De esa manera, simplemente puede llamar a localStorage.setItem(key,value)
y localStorage.getItem(key)
como lo haría normalmente.
No he probado esto extensivamente, pero parece funcionar sin problemas para un pequeño proyecto con el que he estado haciendo pequeños retoques.
Storage.prototype._setItem = Storage.prototype.setItem;
Storage.prototype.setItem = function(key, value)
{
this._setItem(key, JSON.stringify(value));
}
Storage.prototype._getItem = Storage.prototype.getItem;
Storage.prototype.getItem = function(key)
{
try
{
return JSON.parse(this._getItem(key));
}
catch(e)
{
return this._getItem(key);
}
}
Una pequeña mejora en una variant :
Storage.prototype.setObject = function(key, value) {
this.setItem(key, JSON.stringify(value));
}
Storage.prototype.getObject = function(key) {
var value = this.getItem(key);
return value && JSON.parse(value);
}
Debido a la evaluación de cortocircuito , getObject()
devolverá inmediatamente null
si la key
no está en Almacenamiento. Tampoco lanzará una excepción SyntaxError
si el value
es ""
(la cadena vacía; JSON.parse()
no puede manejar eso).
Usando objetos JSON para almacenamiento local:
//CONJUNTO
var m={name:''Hero'',Title:''developer''};
localStorage.setItem(''us'', JSON.stringify(m));
//OBTENER
var gm =JSON.parse(localStorage.getItem(''us''));
console.log(gm.name);
// Iteración de todas las claves y valores de almacenamiento local
for (var i = 0, len = localStorage.length; i < len; ++i) {
console.log(localStorage.getItem(localStorage.key(i)));
}
// BORRAR
localStorage.removeItem(''us'');
delete window.localStorage["us"];
http://rhaboo.org es una capa de azúcar localStorage que te permite escribir cosas como esta:
var store = Rhaboo.persistent(''Some name'');
store.write(''count'', store.count ? store.count+1 : 1);
store.write(''somethingfancy'', {
one: [''man'', ''went''],
2: ''mow'',
went: [ 2, { mow: [''a'', ''meadow'' ] }, {} ]
});
store.somethingfancy.went[1].mow.write(1, ''lawn'');
No usa JSON.stringify / parse porque eso sería inexacto y lento en objetos grandes. En su lugar, cada valor terminal tiene su propia entrada localStorage.
Probablemente puedas adivinar que podría tener algo que ver con rhaboo ;-)
Adrian
Digamos que tienes la siguiente serie llamada películas:
var movies = ["Reservoir Dogs", "Pulp Fiction", "Jackie Brown",
"Kill Bill", "Death Proof", "Inglourious Basterds"];
Usando la función Stringify, su matriz de películas se puede convertir en una cadena usando la siguiente sintaxis:
localStorage.setItem("quentinTarantino", JSON.stringify(movies));
Tenga en cuenta que mis datos se almacenan bajo la clave llamada quentinTarantino.
Recuperar sus datos
var retrievedData = localStorage.getItem("quentinTarantino");
Para convertir de una cadena a un objeto, use la función de análisis JSON:
var movies2 = JSON.parse(retrievedData);
Puede llamar a todos los métodos de matriz en sus películas2
Bucle a través del almacenamiento local
var retrievedData = localStorage.getItem("MyCart");
retrievedData.forEach(function (item) {
console.log(item.itemid);
});
Creo que para evitar este tipo de problema en las cookies locales, de sesión, puede usar la biblioteca opendb ...
En donde puedes resolver esto usando este fragmento de código.
// for set object in db
db.local.setJSON("key", {name: "xyz"});
// for get object form db
db.local.getJSON("key");
https://github.com/pankajbisht/openDB
Para más detalles sobre el almacenamiento web, puede leer el artículo sobre almacenamiento web .
Un pequeño ejemplo de una biblioteca que usa localStorage para realizar un seguimiento de los mensajes recibidos de los contactos:
// This class is supposed to be used to keep a track of received message per contacts.
// You have only four methods:
// 1 - Tells you if you can use this library or not...
function isLocalStorageSupported(){
if(typeof(Storage) !== "undefined" && window[''localStorage''] != null ) {
return true;
} else {
return false;
}
}
// 2 - Give the list of contacts, a contact is created when you store the first message
function getContacts(){
var result = new Array();
for ( var i = 0, len = localStorage.length; i < len; ++i ) {
result.push(localStorage.key(i));
}
return result;
}
// 3 - store a message for a contact
function storeMessage(contact, message){
var allMessages;
var currentMessages = localStorage.getItem(contact);
if(currentMessages == null){
var newList = new Array();
newList.push(message);
currentMessages = JSON.stringify(newList);
}
else
{
var currentList =JSON.parse(currentMessages);
currentList.push(message);
currentMessages = JSON.stringify(currentList);
}
localStorage.setItem(contact, currentMessages);
}
// 4 - read the messages of a contact
function readMessages(contact){
var result = new Array();
var currentMessages = localStorage.getItem(contact);
if(currentMessages != null){
result =JSON.parse(currentMessages);
}
return result;
}