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>