Stockage d'objets dans HTML5 localStorage

2512

J'aimerais stocker un objet JavaScript en HTML5 localStorage, mais mon objet est apparemment en train d'être converti en chaîne.

Je peux stocker et récupérer des types et des tableaux JavaScript primitifs à l'aide 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 la setItemméthode convertit l'entrée en chaîne avant de la stocker.

Je vois ce comportement dans Safari, Chrome et Firefox, donc je suppose que c'est ma mauvaise compréhension de la spécification HTML5 Web Storage , pas un bug ou une limitation spécifique au navigateur.

J'ai essayé de comprendre l' algorithme de clone structuré décrit dans http://www.w3.org/TR/html5/infrastructure.html . Je ne comprends pas bien ce qu'il dit, mais mon problème est peut-être lié au fait que les propriétés de mon objet ne sont pas énumérables (???)

Existe-t-il une solution de contournement facile?


Mise à jour: Le W3C a finalement changé d'avis sur la spécification du clone structuré et a décidé de modifier la spécification pour qu'elle corresponde 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.

Kristopher Johnson
la source
17
BTW, votre lecture de "l'algorithme de clone structuré" est correcte, c'est juste que la spécification a été changée des valeurs de chaîne uniquement en ceci après la fin des implémentations. J'ai déposé le bug bugzilla.mozilla.org/show_bug.cgi?id=538142 avec mozilla pour suivre ce problème.
Nickolay
2
Cela semble être un travail pour indexedDB ...
markasoftware
1
Que diriez-vous de stocker un tableau d'objets dans localStorage? Je suis confronté au même problème qu'il est converti en chaîne.
Jayant Pareek
1
pourriez-vous plutôt sérialiser le tableau? comme stocker avec JSON stringify puis analyser à nouveau lors du chargement?
Brandito
1
Vous pouvez utiliser localDataStorage pour stocker de manière transparente les types de données javascript (tableau, booléen, date, flottant, entier, chaîne et objet)
Mac

Réponses:

3174

En regardant à nouveau Apple , Mozilla et Mozilla 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 filtrer votre objet avant de le stocker, puis de l'analyser ultérieurement lorsque 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));
CMS
la source
160
observez que toutes les métadonnées seront supprimées. vous obtenez simplement un objet avec les paires clé-valeur, donc tout objet ayant un comportement doit être reconstruit.
oligofren
5
@CMS peut setItem lever une exception si les données dépassent la capacité?
Ashish Negi
3
... s'applique uniquement aux objets avec des références circulaires, JSON.stringify()étend l'objet référencé à son "contenu" complet (implicitement stringifié) dans l'objet que nous stringifions. Voir: stackoverflow.com/a/12659424/2044940
CodeManX
3
Le problème avec cette approche sont des problèmes de performances, si vous devez gérer de grands tableaux ou objets.
Mark
3
@oligofren true, mais comme maja l'a correctement suggéré eval () =>, c'est l'une des bonnes utilisations de, vous pouvez facilement récupérer le code de fonction => le stocker sous forme de chaîne, puis eval () le renvoyer :)
jave.web
621

Une amélioration mineure sur 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 l' évaluation de court-circuit , getObject()va immédiatement revenir nullsi keyn'est pas dans le stockage. Il ne lèvera pas non plus d' SyntaxErrorexception if valueis ""(la chaîne vide; JSON.parse()ne peut pas gérer cela).

Guria
la source
48
Je veux juste ajouter rapidement l'utilisation car elle n'était pas immédiatement claire pour moi: var userObject = { userId: 24, name: 'Jack Bauer' }; Et pour la définir localStorage.setObject('user', userObject); Ensuite, récupérez-la du stockage userObject = localStorage.getObject('user'); Vous pouvez même stocker un tableau d'objets si vous le souhaitez.
zuallauz
8
C'est juste une expression booléenne. La deuxième partie n'est évaluée que si celle de gauche est vraie. Dans ce cas, le résultat de l'expression entière proviendra de la partie droite. C'est une technique populaire basée sur la façon dont les expressions booléennes sont évaluées.
Guria
4
Je ne vois pas l'intérêt de la variable locale et de l'évaluation du raccourci ici (améliorations mineures des performances mises à part). Si keyn'est pas dans le stockage local, window.localStorage.getItem(key)retourne null- il ne lève pas d' exception "Accès illégal" - et JSON.parse(null)retourne nullégalement - il ne lève pas non plus d'exception, ni dans Chromium 21 ni dans ES 5.1 section 15.12.2 , car String(null) === "null"qui peut être interprété comme un littéral JSON .
PointedEars
6
Les valeurs dans le stockage local sont toujours des valeurs de chaîne primitives. Donc, ce que cette évaluation de raccourci gère, c'est quand quelqu'un a stocké ""(la chaîne vide) avant. Parce qu'il convertit en type falseet JSON.parse(""), ce qui SyntaxErrordéclencherait une exception, n'est pas appelé.
PointedEars
2
Cela ne fonctionnera pas dans IE8, vous êtes donc préférable d'utiliser les fonctions de la réponse confirmée si vous avez besoin de le prendre en charge.
Ezeke
220

Vous pourriez trouver utile d'étendre l'objet Storage 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 les fonctionnalités que vous vouliez vraiment, même si sous l'API ne prend en charge que les chaînes.

Justin Voskuhl
la source
13
Emballer l'approche du CMS dans une fonction est une bonne idée, il suffit d'un test de fonctionnalité: un pour JSON.stringify, un pour JSON.parse et un pour tester si localStorage peut en fait définir et récupérer un objet. La modification des objets hôtes n'est pas une bonne idée; Je préfère voir cela comme une méthode distincte et non comme localStorage.setObject.
Garrett
4
Cela getObject()lèvera une SyntaxErrorexception si la valeur stockée est "", car JSON.parse()ne peut pas gérer cela. Voir ma modification de la réponse de Guria pour plus de détails.
PointedEars
9
Juste mes deux cents, mais je suis sûr que ce n'est pas une bonne idée d'étendre les objets fournis par le vendeur comme celui-ci.
Sethen
73

L'extension de l'objet Storage est une solution géniale. Pour mon API, j'ai créé une façade pour localStorage, puis 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;
  }
}
Alex Grande
la source
1
C'était presque exactement ce dont j'avais besoin. Il suffit d'ajouter if (valeur == null) {return false} avant le commentaire, sinon cela a entraîné une erreur lors de la vérification de l'existence d'une clé sur localStorage.
Francesco Frapporti
2
C'est plutôt cool en fait. D'accord avec @FrancescoFrapporti, vous avez besoin d'un if pour les valeurs nulles. J'ai également ajouté un '|| value [0] == "[" 'test dans le cas où il y a un tableau dedans.
rob_james
Bon point, je vais éditer ceci. Bien que vous n'ayez pas besoin de la partie nulle, mais si vous le faites, je recommande trois ===. Si vous utilisez JSHint ou JSLint, vous serez averti de ne pas utiliser ==.
Alex Grande
3
Et pour les non-ninjas (comme moi), quelqu'un pourrait-il fournir un exemple d'utilisation pour cette réponse? Est-ce data.set('username': 'ifedi', 'fullname': { firstname: 'Ifedi', lastname: 'Okonkwo'});:?
Ifedi Okonkwo
Oui en effet! Quand j'ai surmonté mon désir d'être nourri à la cuillère, j'ai pris le code pour le tester et l'ai obtenu. Je pense que cette réponse est excellente car 1) Contrairement à la réponse acceptée, il faut du temps pour faire certains contrôles sur les données de chaîne, et 2) Contrairement à la suivante, elle ne va pas étendre un objet natif.
Ifedi Okonkwo
64

Stringify ne résout pas tous les problèmes

Il semble que les réponses ici ne couvrent pas tous les types possibles en JavaScript, voici donc quelques exemples sur la façon de 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 de stocker des fonctions car le eval()mal peut entraîner des problèmes de sécurité, d'optimisation et de débogage. En général,eval() ne doit jamais être utilisé dans du code JavaScript.

Membres privés

Le problème avec l'utilisation JSON.stringify()pour stocker des objets est que cette fonction ne peut pas sérialiser les membres privés. Ce problème peut être résolu en remplaçant la .toString()méthode (qui est appelée implicitement lors du stockage de 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 stringifyne peut pas être résolu sont les références circulaires:

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

Dans cet exemple, JSON.stringify()lancera une TypeError "Conversion de structure circulaire en JSON" . Si le stockage de références circulaires doit être pris en charge, le deuxième paramètre de JSON.stringify()peut ê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 à résoudre, et la restauration de ces données n'est pas non plus triviale.

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

maja
la source
2
Par conséquent, et il va sans dire que le stockage des données dans le stockage doit être basé sur la seule prémisse de copies de données simples. Pas des objets vivants.
Roko C. Buljan
51

Il existe une excellente bibliothèque qui englobe de nombreuses solutions, elle 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")
JProgrammer
la source
2
@SuperUberDuper jStorage nécessite Prototype, MooTools ou jQuery
JProgrammer
28

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 elle dépend de l'implémentation .

aster_x
la source
1
La sérialisation / désérialisation des fonctions n'est pas fiable car elle dépend de l'implémentation . En outre, vous souhaitez remplacer c.f[k] = escape(a[k]); par Unicode-safe c.f[k] = encodeURIComponent(a[k]);et eval('b.' + k + ' = ' + unescape(data.f[k]));par b[k] = eval("(" + decodeURIComponent(data.f[k]) + ")");. Les parenthèses sont obligatoires car votre fonction, si elle est sérialisée correctement, est susceptible d'être anonyme, ce qui n'est pas tel quel / Statement / (donc eval()) lèverait une SyntaxErrorexception sinon).
PointedEars
Et typeofest un opérateur , ne l'écrivez pas comme s'il s'agissait d'une fonction. Remplacez typeof(a[k])par typeof a[k].
PointedEars
En plus d'appliquer mes suggestions et de souligner le manque de fiabilité de l'approche, j'ai corrigé les bugs suivants: 1. Toutes les variables n'ont pas été déclarées. 2. for- inn'a pas été filtré pour ses propres propriétés. 3. Le style de code, y compris le référencement, était incohérent.
PointedEars
@PointedEars quelle différence pratique cela fait-il? la spécification dit que the use and placement of white space, line terminators, and semicolons within the representation String is implementation-dependent. je ne vois aucune différence fonctionnelle.
Michael
@Michael La partie que vous avez citée commence par Note *in particular* that …. Mais la spécification de la valeur de retour commence par An implementation-dependent representation of the function is returned. This representation has the syntax of a FunctionDeclaration.La valeur de retour peut être function foo () {}- en supposant une implémentation conforme .
PointedEars
22

Je suis arrivé à ce message après avoir cliqué sur un autre message qui a été fermé en double - intitulé `` comment stocker un tableau dans le stockage local? ''. Ce qui est bien, sauf qu'aucun des threads ne fournit réellement une réponse complète quant à la façon dont vous pouvez maintenir un tableau dans localStorage - cependant j'ai réussi à créer une solution basée sur les informations contenues dans les deux threads.

Donc, si quelqu'un d'autre veut pouvoir pousser / pop / shift des éléments dans un tableau, et qu'ils veulent que le tableau soit stocké dans localStorage ou même sessionStorage, vous allez ici:

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 - stockage de 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 courantes 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
Andy Lorenz
la source
Il s'agit d'un ensemble très pratique de méthodes pour manipuler des tableaux stockés dans localStorage ou sessionStorage, et mérite beaucoup plus de crédit qu'il n'est attiré. @Andy Lorenz Merci d'avoir pris le temps de partager!
Velojet
6

Vous pouvez utiliser localDataStorage pour stocker de manière transparente les types de données javascript (tableau, booléen, date, flottant, entier, chaîne et objet). Il fournit également une obfuscation légère des données, comprime automatiquement les chaînes, facilite la requête par clé (nom) ainsi que la requête par (clé) valeur, et aide à appliquer le stockage partagé segmenté dans le même domaine en préfixant les clés.

[DISCLAIMER] Je suis l'auteur de l'utilitaire [/ DISCLAIMER]

Exemples:

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

Comme vous pouvez le voir, les valeurs primitives sont respectées.

Mac
la source
1
Ceci est une ressource brillante et juste ce dont j'ai besoin. Je fais des applications Ionic avec AngularJS où je dois enregistrer certains objets javascript dans localStorage et jusqu'à présent, je viens de faire JSON.parse et JSON.stringify, et ils fonctionnent, mais c'est un peu plus lourd que de pouvoir d'utiliser simplement un utilitaire comme celui-ci. Je vais l'essayer.
Nmuta
4

Une autre option serait d'utiliser un plugin existant.

Par exemple, persisto est un projet open source qui fournit une interface facile à localStorage / sessionStorage et automatise la persistance des champs de formulaire (entrée, boutons radio et cases à cocher).

fonctionnalités persisto

(Avertissement: je suis l'auteur.)

mar10
la source
Je travaille toujours sur mon fichier Lisez-moi, mais ma version ne nécessite pas jQuery, comme il semble persister, mais elle fournit une alternative pour traiter les objets de l'élément jQuery. J'en ajouterai plus dans un proche avenir, au fur et à mesure que je travaillerai avec, pour l'aider à gérer davantage différents objets jQuery et à maintenir des choses comme les données persistantes. Aussi, +1 pour avoir essayé de fournir une solution plus simple! En outre, il utilise toutes les méthodes traditionnelles de localStroage; exp: var lsh = new localStorageHelper(); lsh.setItem('bob', 'bill'); comprend également les événements.
SpYk3HH
4

Vous pouvez utiliser ejson pour stocker les objets sous forme de chaînes.

EJSON est une extension de JSON pour prendre en charge plus de types. Il prend en charge tous les types sûrs JSON, ainsi que:

Toutes les sérialisations EJSON sont également des JSON valides. Par exemple, un objet avec une date et un tampon binaire serait sérialisé dans EJSON comme:

{
  "d": {"$date": 1358205756553},
  "b": {"$binary": "c3VyZS4="}
}

Voici mon wrapper localStorage utilisant ejson

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

J'ai ajouté quelques types à mon wrapper, y compris des expressions régulières et des fonctions

Tony Brix
la source
2

J'ai fait un autre wrapper minimaliste avec seulement 20 lignes de code pour permettre de l'utiliser comme il se doit:

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
la source
2

Pour les utilisateurs Typescript désireux de définir et d'obtenir des propriétés typées:

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

Exemple d'utilisation :

// 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
la source
2

https://github.com/adrianmay/rhaboo est une couche de sucre localStorage qui vous permet d'écrire des choses comme ceci:

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

Il n'utilise pas JSON.stringify / parse car cela serait inexact et lent sur les gros objets. Au lieu de cela, chaque valeur de terminal a sa propre entrée localStorage.

Vous pouvez probablement deviner que je pourrais avoir quelque chose à voir avec le rhaboo.

Adrian May
la source
1

Voici une version étendue du code posté par @danott

Il va également mettre en œuvre suppression valeur de localStorage et montre comment ajoute une couche getter et setter donc au lieu de

localstorage.setItem(preview, true)

tu peux écrire

config.preview = true

D'accord, c'était parti:

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

Eh bien, vous pouvez supprimer la partie des alias .bind(...). Cependant je viens de le mettre car c'est vraiment bon de savoir à ce sujet. J'ai mis des heures à découvrir pourquoi un simple get = localStorage.getItem;ne marche pas

Nadu
la source
1

J'ai créé une chose qui ne casse pas les objets de stockage existants, mais crée un wrapper pour que vous puissiez faire ce que vous voulez. Le résultat est un objet normal, sans méthodes, avec accès comme n'importe quel objet.

La chose que j'ai faite.

Si vous voulez que 1 localStoragepropriété soit magique:

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

Si vous en avez besoin de plusieurs:

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

Tout ce que vous faites propou les objets à l' intérieur storage seront automatiquement enregistrés localStorage. Vous jouez toujours avec un vrai objet, vous pouvez donc faire des trucs comme ça:

storage.data.list.push('more data');
storage.another.list.splice(1, 2, {another: 'object'});

Et chaque nouvel objet à l' intérieur d' un objet suivi sera automatiquement suivi.

Le très gros inconvénient: cela dépend Object.observe()donc le support du navigateur est très limité. Et il ne semble pas que cela arrivera bientôt pour Firefox ou Edge.

Rudie
la source
1

Vous ne pouvez pas stocker de valeur de clé sans Format de chaîne .

LocalStorage ne prend en charge que le format String pour la clé / valeur.

C'est pourquoi vous devez convertir vos données en chaîne quel qu'il soit Tableau ou Objet .

Pour stocker des données dans localStorage, commencez par les filtrer en utilisant la méthode JSON.stringify () .

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

Ensuite, lorsque vous souhaitez récupérer des données, vous devez à nouveau analyser la chaîne en objet.

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

J'espère que cela aide.

Moshiur Rahman
la source
0

Pour stocker un objet, vous pouvez créer des lettres que vous pouvez utiliser pour obtenir un objet d'une chaîne vers un objet (cela peut ne pas avoir de sens). Par exemple

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

Cette technique provoquera quelques problèmes si vous utilisez la lettre que vous avez utilisée pour diviser l'objet, et elle est également très expérimentale.

Seizefire
la source
0

J'ai trouvé un moyen de le faire fonctionner avec des objets qui ont des références cycliques.

Faisons un objet avec des références cycliques.

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;

Nous ne pouvons pas le faire JSON.stringifyici, à cause des références circulaires.

oncle circulaire

LOCALSTORAGE.CYCLICJSONa .stringifyet .parsecomme d'habitude JSON, mais fonctionne avec des objets avec des références circulaires. ("Works" signifie parse (stringify (obj)) et obj sont profondément égaux ET ont des ensembles identiques d '"égalités internes")

Mais nous pouvons simplement utiliser les raccourcis:

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

Ensuite, recoveredsera "le même" à obj, dans le sens suivant:

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

Voici l'implémentation 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
la source
-2

localStorage.setItem ('utilisateur', JSON.stringify (utilisateur));

Ensuite, pour le récupérer dans le magasin et le reconvertir en objet:

var user = JSON.parse (localStorage.getItem ('user'));

Si nous devons supprimer toutes les entrées du magasin, nous pouvons simplement faire:

localStorage.clear ();

manasa woddeyar manu
la source
3
C'est une question vieille de 10 ans. Pensez-vous que votre réponse ajoute quelque chose qui n'est pas déjà couvert par les autres réponses?
Kristopher Johnson