web-dev-qa-db-fra.com

json.stringify ne traite pas les méthodes d'objet

J'essaie de développer une application HTML5 hors ligne qui devrait fonctionner dans la plupart des navigateurs modernes (Chrome, Firefox, IE 9+, Safari, Opera). Comme IndexedDB n'est pas (encore) pris en charge par Web et que WebSQL est obsolète, j'ai décidé d'utiliser localStorage pour stocker les objets JavaScript générés par l'utilisateur et JSON.stringify()/JSON.parse() pour insérer ou extraire les objets. Cependant, j'ai découvert que JSON.stringify() ne gère pas les méthodes. Voici un exemple d'objet avec une méthode simple:

    var myObject = {};
    myObject.foo = 'bar';
    myObject.someFunction = function () {/*code in this function*/}

Si je stringifie cet objet (et le mets plus tard dans localStorage), tout ce qui sera conservé sera myObject.foo, pas myObject.someFunction().

    //put object into localStorage
    localStorage.setItem('myObject',JSON.stringify(myObject));

    //pull it out of localStorage and set it to myObject
    myObject = localStorage.getItem('myObject');

    //undefined!
    myObject.someFunction

Je suis sûr que beaucoup d'entre vous connaissent probablement déjà cette limitation/fonctionnalité/comme vous voulez l'appeler. La solution de contournement que j'ai proposée consiste à créer un objet avec les méthodes (myObject = new objectConstructor()), à extraire les propriétés de l'objet de localStorage et à les affecter au nouvel objet que j'ai créé. Je pense que c'est une approche détournée, mais je suis nouveau dans le monde de JavaScript, c'est donc comme cela que je l'ai résolu. Voici donc ma grande question: je voudrais que l’objet entier (propriétés + méthodes) soit inclus dans localStorage. Comment puis-je faire cela? Si vous pouviez peut-être me montrer un meilleur algorithme, ou peut-être une autre méthode JSON que je ne connais pas, je l'apprécierais beaucoup.

18
user2649759

Les fonctions en javascript sont plus que leur code. Ils ont aussi une portée. Le code peut être stratifié, mais la portée ne le peut pas.

JSON.stringify() encodera les valeurs prises en charge par JSON. Les objets avec des valeurs qui peuvent être des objets, des tableaux, des chaînes, des nombres et des booléens. Tout le reste sera ignoré ou jettera des erreurs. Les fonctions ne constituent pas une entité prise en charge dans JSON. JSON gère uniquement les données pures. Les fonctions ne sont pas des données, mais un comportement avec une sémantique plus complexe.


Cela dit, vous pouvez changer le fonctionnement de JSON.stringify(). Le deuxième argument est une fonction replacer. Vous pouvez donc forcer le comportement que vous souhaitez en forçant la strinigification de fonctions:

var obj = {
  foo: function() {
    return "I'm a function!";
  }
};

var json = JSON.stringify(obj, function(key, value) {
  if (typeof value === 'function') {
    return value.toString();
  } else {
    return value;
  }
});

console.log(json);
// {"foo":"function () { return \"I'm a function!\" }"}

Mais lorsque vous relisez cette information, vous devez évaluer la chaîne de fonction et redéfinir le résultat sur l'objet, car JSON ne prend pas en charge les fonctions .


Toutes les fonctions d'encodage en JSON peuvent devenir très difficiles. Es-tu sûr de vouloir faire ça? Il y a probablement un meilleur moyen ...

Peut-être pourriez-vous plutôt sauvegarder les données brutes et les transmettre à un constructeur de votre JS chargé sur la page. localStorage ne contiendrait que les données, mais votre code chargé sur la page fournirait les méthodes pour utiliser ces données.

// contrived example...

var MyClass = function(data) {
  this.firstName = data.firstName;
  this.lastName = data.lastName;
}

MyClass.prototype.getName() {
  return this.firstName + ' ' + this.lastName;
}

localStorage.peopleData = [{
  firstName: 'Bob',
  lastName:  'McDudeFace'
}];

var peopleData = localStorage.peopleData;

var bob = new MyClass(peopleData[0]);
bob.getName() // 'Bob McDudeFace'

Il n'est pas nécessaire de sauvegarder la méthode getName() dans localStorage. Nous avons juste besoin d'alimenter ces données dans un constructeur qui fournira cette méthode.

34
Alex Wayne

Si vous voulez stringifier vos objets, mais qu'ils ont des fonctions, vous pouvez utiliser JSON.stringify() avec le deuxième paramètre replacer. Pour éviter les dépendances cycliques sur les objets, vous pouvez utiliser un var cache = [].

Dans notre projet, nous utilisons lodash . Nous utilisons la fonction suivante pour générer des journaux. Peut être utilisé pour sauvegarder des objets dans localStorage.

var stringifyObj = function(obj) {
  var cache = []
  return JSON.stringify(obj, function(key, value) {
    if (
      _.isString(value) ||
      _.isNumber(value) ||
      _.isBoolean(value)
    ) {
      return value
    } else if (_.isError(value)) {
      return value.stack || ''
    } else if (_.isPlainObject(value) || _.isArray(value)) {
      if (cache.indexOf(value) !== -1) {
        return
      } else {
        // cache each item 
        cache.Push(value)
        return value
      }
    }
  })
}

// create a circular object
var circularObject = {}
circularObject.circularObject = circularObject

// stringify an object
$('body').text(
  stringifyObj(
    {
      myBooblean: true,
      myString: 'foo',
      myNumber: 1,
      myArray: [1, 2, 3],
      myObject: {},
      myCircularObject: circularObject,
      myFunction: function () {}
    }
  )
)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
0
Miguel del Mazo