Когда появился localStorage я был рад, как слон, но, немногим позже, после подробного изучения вопроса, я несколько разочаровался: там могут храниться только строки, а об объектах можно было забыть. Приходилось превращать их в строки для хранения, приходилось обратно конвертировать строку в объект для работы с ним, затем опять конвертировать в строку, чтоб сохранить. Не спорю, есть замечательные библиотеки, позволяющие манипулировать хранилищем максимально просто, но вызывать функции как-то не очень хотелось.
localStorage = { a: {b: 1}, c: { d:2 }}
localStorage.a.b = 3;
Круто было бы, не так ли?
Относительно давно, где-то около полугода назад я задался вопросом: как же, блин, сделать так, чтоб можно было работать с localStorage вообще без функций, чисто как с объектом. Challenge accepted!
Как говорит небезызвестный Геша: “Всё, я здзелал”.
Главной подзадачей было изобретение способа повесить сеттер не только на сам объект, но и на подобъекты, причем ключи заранее не известны. Это решается крайне просто: ставим геттер на объект:
window.objectLocalStorage = {};
Object.defineProperty( window, 'objectLocalStorage', {
get: function() {...}
});
Теперь, когда мы присваиваем что-либо какому-либо из ключей,
window.objectLocalStorage.a = 1;
Вызывается геттер.
Итак, всё до безобразия просто.
( function() {
// объект, который будет хранить данные, пока окно браузера не перезагрузят
// берем данные из хранилища в виде json и парсим их
var _objectLocalStorage = JSON.parse( localStorage.getItem( 'objectStorage' ) ) || {};
// определяем объект с именем objectLocalStorage в window и добавляем ему геттер и сеттер
// во избежание недоразумений, мы не трогаем localStorage, он каким был, тактим остаётся
Object.defineProperty( window, 'objectLocalStorage', {
get: function() {
// для сохранения объекта после присваивания
setTimeout( function(){
var stringified = JSON.stringify( _objectLocalStorage );
// некое подобие оптимизации: если данные в объекте не изменились,
// значит присваивания никакого не было, сработал обычный гет
if( stringified !== localStorage.getItem( 'objectStorage' ) ) {
// сохраняем
localStorage.setItem( 'objectStorage', stringified) );
}
}, 0);
return _objectLocalStorage;
},
// на случай, если objectLocalStorage присвоили целый объект
set: function( v ) {
_objectLocalStorage = v;
localStorage.setItem( 'objectStorage', JSON.stringify( _objectLocalStorage ) );
}
} );
})();
Как пользоваться?
Вставляем код выше в свой js файл, и используем:
objectLocalStorage = { a: 4, b: {c: 2} };
objectLocalStorage.b.c = {d: 5}
Перезагружаем страницу,
console.log( objectLocalStorage ); // { a: 4, b: {c: {d: 5}} }
Как это работает?
При гете objectLocalStorage возвращается локальный объект _objectLocalStorage. Значит, когда мы присваиваем что-нибудь одному из ключей подобъекта в objectLocalStorage, возвращается _objectLocalStorage и присваивание идет в него. То есть
objectLocalStorage.a.b = 5;
аналогично
_objectLocalStorage.a.b = 5;
Но, в первом случае, после присваивания, вызывается сохранение объекта в localStorage. Если не поместить эту часть в таймаут, то сохранение в хранилище будет до присваивания. Согласен, такой способ чреват багами, но другого способа сохранять после, я не нашел.
Вот, собственно, и всё. В идеале, конечно, хорошо бы иметь:
- Поддержку IE < 9. Как известно, Object.defineProperty не кроссбраузерен
- Навешивание геттера на все дочерние объекты, то есть, сейчас:
objectLocalStorage.a.b = 5; // сработает a = objectLocalStorage.a; a.b = 5; // не сработает
- Сделать то же самое, но для sessionStorage
Но это потом, я просто хотел поделиться радостью :)
Лучей бобра вам.
Автор: Finom