Question Stockage d'objets dans HTML5 localStorage


J'aimerais stocker un objet JavaScript dans HTML5 localStorage, mais mon objet est apparemment converti en une chaîne.

Je peux stocker et récupérer des types et des tableaux JavaScript primitifs en utilisant localStorage, mais les objets ne semblent pas fonctionner. Devraient-ils?

Voici mon code:

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 sortie de la console est

typeof testObject: object
testObject properties:
  one: 1
  two: 2
  three: 3
typeof retrievedObject: string
Value of retrievedObject: [object Object]

Il me semble que le setItem La méthode convertit l'entrée en une chaîne avant de la stocker.

Je vois ce comportement dans Safari, Chrome et Firefox, donc je suppose que c'est mon incompréhension de la Stockage Web HTML5 spec, pas un bug spécifique au navigateur ou une limitation.

J'ai essayé de donner un sens à la clone structuré algorithme décrit dans http://www.w3.org/TR/html5/infrastructure.html. Je ne comprends pas tout à fait ce que ça veut dire, mais peut-être que mon problème est lié au fait que les propriétés de mon objet ne sont pas énumérables (???)

Y a-t-il une solution de contournement facile?


Mise à jour: Le W3C a finalement changé d'avis sur la spécification de clone structuré, et a décidé de changer la spécification pour correspondre aux implémentations. Voir https://www.w3.org/Bugs/Public/show_bug.cgi?id=12111. Cette question n'est donc plus valable à 100%, mais les réponses peuvent toujours être intéressantes.


2051
2018-01-06 04:05


origine


Réponses:


En regardant le Pomme, Mozilla et Microsoft documentation, la fonctionnalité semble être limitée pour gérer uniquement les paires clé / valeur de chaîne.

Une solution de contournement peut être de stringifier votre objet avant de le stocker, et plus tard l'analyser quand vous le récupérez:

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

2650
2018-01-06 04:25



Une amélioration mineure sur un une 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);
}

En raison de évaluation de court-circuit, getObject() volonté immédiatement revenir null si key n'est pas en Stockage. Il ne jettera pas non plus SyntaxError exception si value est "" (la chaîne vide; JSON.parse() ne peut pas gérer cela).


562
2018-06-30 06:45



Vous pourriez trouver utile d'étendre l'objet de stockage avec ces méthodes pratiques:

Storage.prototype.setObject = function(key, value) {
    this.setItem(key, JSON.stringify(value));
}

Storage.prototype.getObject = function(key) {
    return JSON.parse(this.getItem(key));
}

De cette façon, vous obtenez la fonctionnalité que vous vouliez vraiment même si sous l'API ne supporte que les chaînes.


195
2018-01-06 04:42



L'extension de l'objet de stockage est une solution géniale. Pour mon API, j'ai créé une façade pour localStorage et ensuite je vérifie s'il s'agit d'un objet ou non lors de la configuration et de l'obtention.

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

64
2018-01-21 18:29



Il y a une grande bibliothèque qui englobe de nombreuses solutions et prend même en charge les anciens navigateurs appelés jStorage

Vous pouvez définir un objet

$.jStorage.set(key, value)

Et récupérez-le facilement

value = $.jStorage.get(key)
value = $.jStorage.get(key, "default value")

52
2017-08-23 03:52



Stringify ne résout pas tous les problèmes

Il semble que les réponses ne couvrent pas tous les types possibles en JavaScript, alors voici quelques exemples pour les traiter correctement:

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

Je ne recommande pas pour stocker les fonctions parce que eval() Le mal peut conduire à des problèmes de sécurité, d'optimisation et de débogage.         En général, eval() ne devrait jamais être utilisé dans le code JavaScript.

Membres privés

Le problème avec l'utilisation JSON.stringify()pour stocker des objets, cette fonction ne peut pas sérialiser les membres privés. Ce problème peut être résolu en écrasant le .toString() méthode (qui est appelée implicitement lors du stockage des données dans le stockage 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);

Références circulaires

Un autre problème stringify ne peut pas traiter sont des références circulaires:

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

Dans cet exemple, JSON.stringify() jettera un TypeError  "Conversion de structure circulaire en JSON".         Si le stockage des références circulaires doit être pris en charge, le deuxième paramètre de JSON.stringify() pourrait être utilisé:

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

Cependant, trouver une solution efficace pour stocker des références circulaires dépend fortement des tâches qui doivent être résolues, et la restauration de ces données n'est pas triviale non plus.

Il y a déjà une question sur SO traitant de ce problème: Stringify (convertir en JSON) un objet JavaScript avec une référence circulaire


50
2017-11-19 09:51



Utilisation d'objets JSON pour le stockage local:

//ENSEMBLE

var m={name:'Hero',Title:'developer'};
localStorage.setItem('us', JSON.stringify(m));

//OBTENIR

var gm =JSON.parse(localStorage.getItem('us'));
console.log(gm.name);

// Itération de toutes les clés et valeurs de stockage local

for (var i = 0, len = localStorage.length; i < len; ++i) {
  console.log(localStorage.getItem(localStorage.key(i)));
}

// EFFACER

localStorage.removeItem('us');
delete window.localStorage["us"];

29
2017-11-20 07:06



En théorie, il est possible de stocker des objets avec des fonctions:

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

Cependant, la sérialisation / désérialisation des fonctions n'est pas fiable car il dépend de l'implémentation.


27
2018-04-05 21:20



Vous pouvez également remplacer le stockage par défaut setItem(key,value) et getItem(key) méthodes pour gérer les objets / tableaux comme tout autre type de données. De cette façon, vous pouvez simplement appeler localStorage.setItem(key,value) et localStorage.getItem(key) comme vous le feriez normalement.

Je ne l'ai pas testé intensivement, mais il a semblé fonctionner sans problèmes pour un petit projet que j'ai bricolé.

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

22
2018-04-26 13:00



Je suis arrivé à ce post après avoir frappé sur un autre poste qui a été fermé en tant que copie de ce - intitulé «comment stocker un tableau dans localstorage? Ce qui est bien, sauf que les deux threads fournissent réellement une réponse complète quant à la façon dont vous pouvez maintenir un tableau dans localStorage - mais j'ai réussi à concevoir une solution basée sur les informations contenues dans les deux threads.

Donc, si quelqu'un d'autre veut pouvoir pousser / déplacer / déplacer des éléments dans un tableau, et qu'il veut que ce tableau soit stocké dans localStorage ou bien dans sessionStorage, voici ce que vous pouvez faire:

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

exemple d'utilisation - stocker des chaînes simples dans un tableau localStorage:

localStorage.pushArrayItem('myArray','item one');
localStorage.pushArrayItem('myArray','item two');

exemple d'utilisation - stockage d'objets dans le tableau 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éthodes communes pour manipuler les tableaux:

.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

19
2018-05-07 11:35



Recommandez d'utiliser une bibliothèque d'abstraction pour la plupart des fonctionnalités discutées ici ainsi qu'une meilleure compatibilité. Beaucoup d'options:


13
2018-03-12 07:59