Almacenar objetos en HTML5 localStorage

2511

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.

Kristopher Johnson
fuente
17
Por cierto, su lectura del "algoritmo de clonación estructurada" es correcta, es solo que la especificación cambió de valores de solo cadena a esto después de que las implementaciones terminaron. Archivé el error bugzilla.mozilla.org/show_bug.cgi?id=538142 con mozilla para rastrear este problema.
Nickolay
2
Esto parece un trabajo para indexedDB ...
markasoftware
1
¿Qué tal almacenar una matriz de objetos en localStorage? Estoy enfrentando el mismo problema que se está convirtiendo en cadena.
Jayant Pareek
1
¿podrías simplemente serializar la matriz? como almacenar con JSON stringify y luego analizar nuevamente al cargar?
Brandito
1
Puede usar localDataStorage para almacenar de forma transparente los tipos de datos de JavaScript (matriz, booleano, fecha, flotante, entero, cadena y objeto)
Mac

Respuestas:

3173

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:

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));
CMS
fuente
160
observe que se eliminarán los metadatos. solo obtienes un objeto con los pares clave-valor, por lo que cualquier objeto con comportamiento necesita ser reconstruido.
oligofren
55
¿@CMS puede establecer que Item arroje alguna excepción si los datos superan la capacidad?
Ashish Negi
3
... se aplica solo a los objetos con referencias circulares, JSON.stringify()expande el objeto referenciado a su "contenido" completo (implícitamente encadenado) en el objeto que encadenamos. Ver: stackoverflow.com/a/12659424/2044940
CodeManX el
3
El problema con este enfoque son los problemas de rendimiento, si tiene que manejar grandes matrices u objetos.
Mark
3
@oligofren es cierto, pero como maja sugirió correctamente eval () =>, este es uno de los buenos usos, puede recuperar fácilmente el código de función => almacenarlo como cadena y luego evaluarlo () de nuevo :)
jave.web
621

Una mejora menor en una variante :

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()será inmediatamente volver nullsi keyno está en el almacenamiento. Tampoco arrojará una SyntaxErrorexcepción si valuees ""(la cadena vacía; JSON.parse()no puede manejar eso).

Guria
fuente
48
Solo quiero agregar rápidamente el uso, ya que no estaba claro de inmediato para mí: var userObject = { userId: 24, name: 'Jack Bauer' }; y configurarlo localStorage.setObject('user', userObject); Luego recuperarlo del almacenamiento userObject = localStorage.getObject('user'); Incluso puede almacenar una matriz de objetos si lo desea.
zuallauz
8
Es solo una expresión booleana. La segunda parte se evalúa solo si la izquierda es verdadera. En ese caso, el resultado de la expresión completa será de la parte correcta. Es una técnica popular basada en la forma en que se evalúan las expresiones booleanas.
Guria
44
No veo el punto de la variable local y la evaluación de acceso directo aquí (aparte de las mejoras de rendimiento menores). Si keyno está en el almacenamiento local, window.localStorage.getItem(key)los retornos null- lo hace no lanzan una excepción "Acceso ilegal" - y JSON.parse(null)vuelve nullasí - no no una excepción tampoco, ni en cromo 21 ni por ES 5.1 sección 15.12.2 , ya String(null) === "null"que puede ser interpretado como un literal JSON .
PointedEars
66
Los valores en el almacenamiento local son siempre valores de cadena primitivos. Entonces, lo que maneja esta evaluación de acceso directo es cuando alguien almacenó ""(la cadena vacía) antes. Debido a que se convierte de tipo a falsey JSON.parse(""), lo que arrojaría una SyntaxErrorexcepción, no se llama.
PointedEars
2
Esto no funcionará en IE8, por lo que es mejor usar las funciones en la respuesta confirmada si necesita soporte.
Ezeke
220

Puede resultarle útil ampliar el objeto Almacenamiento con estos métodos útiles:

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 forma obtienes la funcionalidad que realmente querías, aunque debajo de la API solo se admiten cadenas.

Justin Voskuhl
fuente
13
Empaquetar el enfoque de CMS en una función es una buena idea, solo necesita pruebas de características: una para JSON.stringify, una para JSON.parse y otra para probar si localStorage puede de hecho establecer y recuperar un objeto. Modificar objetos host no es una buena idea; Prefiero ver esto como un método separado y no como localStorage.setObject.
Garrett
44
Esto getObject()arrojará una SyntaxErrorexcepción si el valor almacenado es "", porque JSON.parse()no puede manejar eso. Vea mi edición de la respuesta de Guria para más detalles.
PointedEars
99
Solo mis dos centavos, pero estoy bastante seguro de que no es una buena idea extender objetos proporcionados por el proveedor de esta manera.
Sethen
73

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.

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;
  }
}
Alex Grande
fuente
1
Esto era casi exactamente lo que necesitaba. Solo tenía que agregar if (value == null) {return false} antes del comentario, de lo contrario, se produjo un error al verificar la existencia de una clave en localStorage.
Francesco Frapporti
2
Esto es bastante bueno en realidad. De acuerdo con @FrancescoFrapporti, necesita un if para valores nulos. También agregué un '|| valor [0] == "[" 'prueba en caso de que haya una matriz allí.
rob_james
Buen punto, editaré esto. Aunque no necesita la parte nula, pero si la necesita le recomiendo tres ===. Si usa JSHint o JSLint, se le advertirá contra el uso de ==.
Alex Grande
3
Y para los no ninjas (como yo), ¿alguien podría proporcionar un ejemplo de uso para esta respuesta? Es data.set('username': 'ifedi', 'fullname': { firstname: 'Ifedi', lastname: 'Okonkwo'});:?
Ifedi Okonkwo
Si de hecho! Cuando superé mi deseo de ser alimentado con una cuchara, tomé el código para probarlo y lo obtuve. Creo que esta respuesta es excelente porque 1) A diferencia de la respuesta aceptada, lleva tiempo hacer ciertas verificaciones en los datos de la cadena, y 2) A diferencia de la siguiente, no se extiende un objeto nativo.
Ifedi Okonkwo
64

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 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):

//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 stringifyque no puede resolver son las referencias circulares:

var obj = {};
obj["circular"] = obj;
localStorage.object = JSON.stringify(obj);  //Fails

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 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 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

maja
fuente
2
Por lo tanto, y no hace falta decir que almacenar datos en el almacenamiento debe basarse en la única premisa de copias de datos simples. No viven objetos.
Roko C. Buljan
51

Hay una gran biblioteca que envuelve muchas soluciones, por lo que incluso admite navegadores 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")
JProgrammer
fuente
2
@SuperUberDuper jStorage requiere Prototype, MooTools o jQuery
JProgrammer
28

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 funciones no es confiable porque depende de la implementación .

aster_x
fuente
1
La serialización / deserialización de funciones no es confiable porque depende de la implementación . Además, desea reemplazar c.f[k] = escape(a[k]); con Unicode-safe c.f[k] = encodeURIComponent(a[k]);y eval('b.' + k + ' = ' + unescape(data.f[k]));con b[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 / (por eval()lo tanto ) arrojaría una SyntaxErrorexcepción de lo contrario).
PointedEars
Y typeofes un operador , no lo escriba como si fuera una función. Reemplazar typeof(a[k])con typeof a[k].
PointedEars
Además de aplicar mis sugerencias y enfatizar la falta de fiabilidad del enfoque, he solucionado los siguientes errores: 1. No se declararon todas las variables. 2. for- inno fue filtrado por propiedades propias. 3. El estilo del código, incluidas las referencias, era inconsistente.
PointedEars
@PointedEars, ¿qué diferencia práctica hace esto? La especificación dice the use and placement of white space, line terminators, and semicolons within the representation String is implementation-dependent. que no veo diferencias funcionales.
Michael
@Michael La parte que citó comienza con Note *in particular* that …. Pero la especificación del valor de retorno comienza con An implementation-dependent representation of the function is returned. This representation has the syntax of a FunctionDeclaration.El valor de retorno puede ser function foo () {}, suponiendo una implementación conforme .
PointedEars
22

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:

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
Andy Lorenz
fuente
Este es un conjunto muy útil de métodos para manipular matrices almacenadas en localStorage o sessionStorage, y merece mucho más crédito del que se atrae. @Andy Lorenz ¡Gracias por tomarse el tiempo para compartir!
Velojet
6

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:

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 puede ver, se respetan los valores primitivos.

Mac
fuente
1
Este es un recurso brillante y justo lo que necesito. Estoy haciendo aplicaciones iónicas con AngularJS donde necesito guardar ciertos objetos de JavaScript en localStorage y hasta ahora he estado haciendo JSON.parse y JSON.stringify, y funcionan, pero es un poco más engorroso que poder simplemente usar una utilidad como esta. Voy a intentarlo
Nmuta
4

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).

características persisto

(Descargo de responsabilidad: soy el autor).

mar10
fuente
Todavía estoy trabajando en mi archivo Léame, pero mi versión no requiere jQuery, como parece que persisto lo hace, pero proporciona una alternativa para tratar con objetos de elementos jQuery. Agregaré más en el futuro cercano, a medida que trabaje más para ayudarlo a manejar más diferentes objetos jQuery y mantener cosas como datos persistentes. Además, ¡+1 por intentar proporcionar una solución más simple! Además, utiliza todos los métodos tradicionales de localStroage; exp: var lsh = new localStorageHelper(); lsh.setItem('bob', 'bill'); también incluye eventos.
SpYk3HH
4

Puede usar ejson para almacenar los objetos como cadenas.

EJSON es una extensión de JSON para admitir más tipos. Admite todos los tipos seguros para JSON, así como:

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 contenedor localStorage usando ejson

https://github.com/UziTech/storage.js

Agregué algunos tipos a mi contenedor, incluidas las expresiones y funciones regulares

Tony Brix
fuente
2

Hice otro contenedor minimalista con solo 20 líneas de código para permitir usarlo 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');

https://github.com/zevero/simpleWebstorage

zevero
fuente
2

Para usuarios de Typecript que deseen 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);
    }
}

Ejemplo de uso :

// 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");
Flavien Volken
fuente
2

https://github.com/adrianmay/rhaboo es una capa de azúcar de 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 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.

Adrian May
fuente
1

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:

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 del alias .bind(...). Sin embargo, lo acabo de poner ya que es realmente bueno saber sobre esto. Me llevó horas descubrir por qué un simple get = localStorage.getItem;no funciona

Nadu
fuente
1

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 localStoragepropiedad sea mágica:

var prop = ObjectStorage(localStorage, 'prop');

Si necesitas varios:

var storage = ObjectStorage(localStorage, ['prop', 'more', 'props']);

Todo lo que hagas propo los objetos dentro storage se guardarán automáticamente 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, 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.

Rudie
fuente
1

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 () .

var myObj = [{name:"test", time:"Date 2017-02-03T08:38:04.449Z"}];
localStorage.setItem('item', JSON.stringify(myObj));

Luego, cuando desee recuperar datos, debe analizar la Cadena a Objeto nuevamente.

var getObj = JSON.parse(localStorage.getItem('item'));

Espero eso ayude.

Moshiur Rahman
fuente
0

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

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á algunos problemas técnicos si usa la letra que usó para dividir el objeto, y también es muy experimental.

Seizefire
fuente
0

Encontré una manera de hacerlo funcionar con objetos que tienen referencias cíclicas.

Hagamos un objeto con referencias cíclicas.

obj = {
    L: {
        L: { v: 'lorem' },
        R: { v: 'ipsum' }
    },
    R: {
        L: { v: 'dolor' },
        R: {
            L: { v: 'sit' },
            R: { v: 'amet' }
        }
    }
}
obj.R.L.uncle = obj.L;
obj.R.R.uncle = obj.L;
obj.R.R.L.uncle = obj.R.L;
obj.R.R.R.uncle = obj.R.L;
obj.L.L.uncle = obj.R;
obj.L.R.uncle = obj.R;

No podemos hacer JSON.stringifyaquí, debido a las referencias circulares.

circularUncle

LOCALSTORAGE.CYCLICJSONtiene .stringifye .parseigual que lo normal JSON, 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:

LOCALSTORAGE.setObject('latinUncles', obj)
recovered = LOCALSTORAGE.getObject('latinUncles')

Entonces, recoveredserá "lo mismo" que obj, en el siguiente sentido:

[
obj.L.L.v === recovered.L.L.v,
obj.L.R.v === recovered.L.R.v,
obj.R.L.v === recovered.R.L.v,
obj.R.R.L.v === recovered.R.R.L.v,
obj.R.R.R.v === recovered.R.R.R.v,
obj.R.L.uncle === obj.L,
obj.R.R.uncle === obj.L,
obj.R.R.L.uncle === obj.R.L,
obj.R.R.R.uncle === obj.R.L,
obj.L.L.uncle === obj.R,
obj.L.R.uncle === obj.R,
recovered.R.L.uncle === recovered.L,
recovered.R.R.uncle === recovered.L,
recovered.R.R.L.uncle === recovered.R.L,
recovered.R.R.R.uncle === recovered.R.L,
recovered.L.L.uncle === recovered.R,
recovered.L.R.uncle === recovered.R
]

Aquí está la implementación de LOCALSTORAGE

LOCALSTORAGE = (function(){
  "use strict";
  var ignore = [Boolean, Date, Number, RegExp, String];
  function primitive(item){
    if (typeof item === 'object'){
      if (item === null) { return true; }
      for (var i=0; i<ignore.length; i++){
        if (item instanceof ignore[i]) { return true; }
      }
      return false;
    } else {
      return true;
    }
  }
  function infant(value){
    return Array.isArray(value) ? [] : {};
  }
  function decycleIntoForest(object, replacer) {
    if (typeof replacer !== 'function'){
      replacer = function(x){ return x; }
    }
    object = replacer(object);
    if (primitive(object)) return object;
    var objects = [object];
    var forest  = [infant(object)];
    var bucket  = new WeakMap(); // bucket = inverse of objects 
    bucket.set(object, 0);    
    function addToBucket(obj){
      var result = objects.length;
      objects.push(obj);
      bucket.set(obj, result);
      return result;
    }
    function isInBucket(obj){ return bucket.has(obj); }
    function processNode(source, target){
      Object.keys(source).forEach(function(key){
        var value = replacer(source[key]);
        if (primitive(value)){
          target[key] = {value: value};
        } else {
          var ptr;
          if (isInBucket(value)){
            ptr = bucket.get(value);
          } else {
            ptr = addToBucket(value);
            var newTree = infant(value);
            forest.push(newTree);
            processNode(value, newTree);
          }
          target[key] = {pointer: ptr};
        }
      });
    }
    processNode(object, forest[0]);
    return forest;
  };
  function deForestIntoCycle(forest) {
    var objects = [];
    var objectRequested = [];
    var todo = [];
    function processTree(idx) {
      if (idx in objects) return objects[idx];
      if (objectRequested[idx]) return null;
      objectRequested[idx] = true;
      var tree = forest[idx];
      var node = Array.isArray(tree) ? [] : {};
      for (var key in tree) {
        var o = tree[key];
        if ('pointer' in o) {
          var ptr = o.pointer;
          var value = processTree(ptr);
          if (value === null) {
            todo.push({
              node: node,
              key: key,
              idx: ptr
            });
          } else {
            node[key] = value;
          }
        } else {
          if ('value' in o) {
            node[key] = o.value;
          } else {
            throw new Error('unexpected')
          }
        }
      }
      objects[idx] = node;
      return node;
    }
    var result = processTree(0);
    for (var i = 0; i < todo.length; i++) {
      var item = todo[i];
      item.node[item.key] = objects[item.idx];
    }
    return result;
  };
  var console = {
    log: function(x){
      var the = document.getElementById('the');
      the.textContent = the.textContent + '\n' + x;
	},
	delimiter: function(){
      var the = document.getElementById('the');
      the.textContent = the.textContent +
		'\n*******************************************';
	}
  }
  function logCyclicObjectToConsole(root) {
    var cycleFree = decycleIntoForest(root);
    var shown = cycleFree.map(function(tree, idx) {
      return false;
    });
    var indentIncrement = 4;
    function showItem(nodeSlot, indent, label) {
      var leadingSpaces = ' '.repeat(indent);
      var leadingSpacesPlus = ' '.repeat(indent + indentIncrement);
      if (shown[nodeSlot]) {
        console.log(leadingSpaces + label + ' ... see above (object #' + nodeSlot + ')');
      } else {
        console.log(leadingSpaces + label + ' object#' + nodeSlot);
        var tree = cycleFree[nodeSlot];
        shown[nodeSlot] = true;
        Object.keys(tree).forEach(function(key) {
          var entry = tree[key];
          if ('value' in entry) {
            console.log(leadingSpacesPlus + key + ": " + entry.value);
          } else {
            if ('pointer' in entry) {
              showItem(entry.pointer, indent + indentIncrement, key);
            }
          }
        });
      }
    }
	console.delimiter();
    showItem(0, 0, 'root');
  };
  function stringify(obj){
    return JSON.stringify(decycleIntoForest(obj));
  }
  function parse(str){
    return deForestIntoCycle(JSON.parse(str));
  }
  var CYCLICJSON = {
    decycleIntoForest: decycleIntoForest,
    deForestIntoCycle : deForestIntoCycle,
    logCyclicObjectToConsole: logCyclicObjectToConsole,
    stringify : stringify,
    parse : parse
  }
  function setObject(name, object){
    var str = stringify(object);
    localStorage.setItem(name, str);
  }
  function getObject(name){
    var str = localStorage.getItem(name);
    if (str===null) return null;
    return parse(str);
  }
  return {
    CYCLICJSON : CYCLICJSON,
    setObject  : setObject,
    getObject  : getObject
  }
})();
obj = {
	L: {
		L: { v: 'lorem' },
		R: { v: 'ipsum' }
	},
	R: {
		L: { v: 'dolor' },
		R: {
			L: { v: 'sit' },
			R: { v: 'amet' }
		}
	}
}
obj.R.L.uncle = obj.L;
obj.R.R.uncle = obj.L;
obj.R.R.L.uncle = obj.R.L;
obj.R.R.R.uncle = obj.R.L;
obj.L.L.uncle = obj.R;
obj.L.R.uncle = obj.R;

// LOCALSTORAGE.setObject('latinUncles', obj)
// recovered = LOCALSTORAGE.getObject('latinUncles')
// localStorage not available inside fiddle ):
LOCALSTORAGE.CYCLICJSON.logCyclicObjectToConsole(obj)
putIntoLS = LOCALSTORAGE.CYCLICJSON.stringify(obj);
recovered = LOCALSTORAGE.CYCLICJSON.parse(putIntoLS);
LOCALSTORAGE.CYCLICJSON.logCyclicObjectToConsole(recovered);

var the = document.getElementById('the');
the.textContent = the.textContent + '\n\n' +
JSON.stringify(
[
obj.L.L.v === recovered.L.L.v,
obj.L.R.v === recovered.L.R.v,
obj.R.L.v === recovered.R.L.v,
obj.R.R.L.v === recovered.R.R.L.v,
obj.R.R.R.v === recovered.R.R.R.v,
obj.R.L.uncle === obj.L,
obj.R.R.uncle === obj.L,
obj.R.R.L.uncle === obj.R.L,
obj.R.R.R.uncle === obj.R.L,
obj.L.L.uncle === obj.R,
obj.L.R.uncle === obj.R,
recovered.R.L.uncle === recovered.L,
recovered.R.R.uncle === recovered.L,
recovered.R.R.L.uncle === recovered.R.L,
recovered.R.R.R.uncle === recovered.R.L,
recovered.L.L.uncle === recovered.R,
recovered.L.R.uncle === recovered.R
]
)
<pre id='the'></pre>

Mathheadinclouds
fuente
-2

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 ();

manasa woddeyar manu
fuente
3
Esta es una pregunta de 10 años. ¿Crees que tu respuesta agrega algo que aún no está cubierto por las otras respuestas?
Kristopher Johnson