Json.stringify не обрабатывает методы объекта

Я пытаюсь создать автономное приложение HTML5, которое должно работать в большинстве современных браузеров (Chrome, Firefox, IE 9+, Safari, Opera). Поскольку IndexedDB не поддерживается Safari (пока), а WebSQL устарел, я решил использовать localStorage для хранения созданных пользователем JavaScript-объектов и JSON.stringify()/JSON.parse() для ввода или вытаскивания объектов. Однако я обнаружил, что JSON.stringify() не обрабатывает методы. Вот пример объекта с простым методом:

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

Если я строю этот объект (и позже поместил его в localStorage), все, что будет сохранено, будет myObject.foo, а не 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

Я уверен, что многие из вас, вероятно, уже знают об этом ограничении/функции/о том, что вы хотите назвать. Обходной путь, который я придумал, - создать объект с помощью методов (myObject = new objectConstructor()), вытащить свойства объекта из localStorage и назначить их для нового объекта, который я создал. Я чувствую, что это окольный подход, но я новичок в мире JavaScript, так вот как я его решил. Итак, вот мой главный вопрос: я бы хотел, чтобы весь объект (свойства + методы) был включен в localStorage. Как мне это сделать? Если вы можете показать мне лучший алгоритм или, может быть, другой метод JSON, о котором я не знаю, я бы очень признателен.

Ответ 1

Функции в javascript - это не просто их код. У них также есть возможности. Код может быть сжат, но область не может.

JSON.stringify() будет кодировать значения, поддерживаемые JSON. Объекты со значениями, которые могут быть объектами, массивами, строками, числами и булевыми. Все остальное будет игнорироваться или бросать ошибки. Функции не поддерживаются в JSON. JSON обрабатывает только чистые данные, функции - это не данные, а поведение с более сложной семантикой.


Это означает, что вы можете изменить способ работы JSON.stringify(). Второй аргумент - это функция replacer. Таким образом, вы можете заставить поведение, которое вы хотите, заставляя стригификацию функций:

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

Но когда вы прочтете это, вам придется анализировать функциональную строку и возвращать результат объекту, потому что JSON не поддерживает функции.


Все функции кодирования в JSON могут стать довольно волосатыми. Вы уверены, что хотите это сделать? Вероятно, есть лучший способ...

Возможно, вы могли бы сохранить необработанные данные и передать это конструктору из вашего JS, загруженного на страницу. localStorage будет содержать только данные, но ваш код, загруженный на страницу, предоставит методы для работы с этими данными.

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

Нам не нужно сохранять метод getName() до localStorage. Нам просто нужно передать эти данные в конструктор, который предоставит этот метод.

Ответ 2

Если вы хотите укрепить свои объекты, но у них есть функции, вы можете использовать JSON.stringify() со вторым параметром replacer. Чтобы предотвратить циклические зависимости от объектов, вы можете использовать var cache = [].

В нашем проекте мы используем lodash. Для генерации журналов мы используем следующую функцию. Может использоваться для сохранения объектов в 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>