Хранение объектов в HTML5 localStorage

Я'хочу сохранить объект JavaScript в HTML5 localStorage, но мой объект, очевидно, преобразуется в строку.

Я могу хранить и извлекать примитивные типы JavaScript и массивы с помощью localStorage, но объекты, похоже, не работают. Должны ли они работать?

Вот мой код:

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

Консольный вывод

typeof testObject: object
testObject properties:
  one: 1
  two: 2
  three: 3
typeof retrievedObject: string
Value of retrievedObject: [object Object]

Мне кажется, что метод setItem преобразует входные данные в строку перед их сохранением.

Я наблюдаю такое поведение в Safari, Chrome и Firefox, поэтому я предполагаю, что это мое непонимание спецификации HTML5 Web Storage, а не ошибка или ограничение конкретного браузера.

Я попытался понять алгоритм структурированного клона, описанный в http://www.w3.org/TR/html5/infrastructure.html. Я не совсем понимаю, о чем там говорится, но, возможно, моя проблема связана с тем, что свойства моего объекта не являются перечислимыми (???).

Есть ли простой обходной путь?


Обновление: W3C в конечном итоге изменил свое мнение о спецификации структурированного клона и решил изменить спецификацию, чтобы она соответствовала реализации. См. https://www.w3.org/Bugs/Public/show_bug.cgi?id=12111. Таким образом, этот вопрос больше не является на 100% актуальным, но ответы на него все еще могут представлять интерес.

Комментарии к вопросу (5)
Решение

Изучая документацию Apple, Mozilla и Mozilla again, кажется, что функциональность ограничена обработкой только строковых пар ключ/значение.

Обходным решением может быть стрингизация вашего объекта перед его сохранением, а затем разбор его при получении:

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));
Комментарии (5)

Небольшое улучшение варианта:

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

Из-за оценки замыкания, getObject() будет немедленно возвращать null, если key не находится в хранилище. Она также не выбросит исключение SyntaxError, если value будет "" (пустая строка; JSON.parse() не может обработать это).

Комментарии (10)

Возможно, вам будет полезно расширить объект Storage этими удобными методами:

Storage.prototype.setObject = function(key, value) {
    this.setItem(key, JSON.stringify(value));
}

Storage.prototype.getObject = function(key) {
    return JSON.parse(this.getItem(key));
}

Таким образом, вы получите функциональность, которая вам действительно нужна, даже если API поддерживает только строки.

Комментарии (3)

Расширение объекта хранения является удивительным решением. Для моих API, я создал фасад для localStorage, а затем проверить, если это объект или нет во время установки и получения.

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;
  }
}
Комментарии (9)

Преобразовать в строки Не'т решить все проблемы

Кажется, что ответы здесь Дон'т охватывают все типы, которые возможны в JavaScript, так вот несколько коротких примеров о том, как правильно бороться с ними:

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

Не рекомендую для хранения функции, потому что функция eval()это зло может привести к вопросам, касающимся безопасности, оптимизации и отладки. В целом, функции eval() не должны использоваться в коде JavaScript.

Частные члены

Проблема с использованием формата JSON.преобразовать в строки () для того, чтобы хранить объекты, то эта функция не может serialise частных членов. Эта проблема может быть решена путем перезаписи .метод toString() метод (который вызывается неявно при хранении данных в веб-хранилище):

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

Циклические ссылки

Преобразовать в строки еще одной проблемой может'т бороться с циклическими ссылками:

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

В этом примере, формат JSON.преобразовать в строки() бросит ошибку TypeError "и преобразование круговой структуры в JSON и". Если хранение циклические ссылки должны быть поддержаны, второго параметра `формат JSON.преобразовать в строки () могут быть использованы:

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

Однако найти эффективное решение для хранения циклических ссылок сильно зависит от задач, которые необходимо решить, и восстановление таких данных не является тривиальной, либо.

Есть уже какой-то вопрос так борюсь с этой проблемой: https://stackoverflow.com/questions/10392293/stringify-javascript-object-with-circular-reference/12659424#12659424

Комментарии (0)

Есть большая библиотека, которая обертывает много решений, поэтому она даже поддерживает старые браузеры называют jStorage

Вы можете установить объект

$.jStorage.set(key, value)

И легко извлечь его

value = $.jStorage.get(key)
value = $.jStorage.get(key, "default value")
Комментарии (2)

В теории, можно хранить объекты с функциями:

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

Однако, функции сериализации/десериализации ненадежны, потому что это является зависящим от реализации.

Комментарии (5)

Я приехал в этот пост после удара на другой пост, который был закрыт, так как является дубликатом этот - под названием 'Как сохранить массив в localStorage?'. Это нормально, только ни один поток на самом деле обеспечивает полный ответ на вопрос, как можно сохранить массив в localStorage - однако мне удалось выработать решение на основе информации, содержащейся в обе нити.

Так что если кто еще хочет быть в состоянии пуш/поп/элементы сдвига в массиве, и они хотят, что массив хранится в localStorage или sessionStorage действительно, здесь вы идете:

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

пример использования - хранения простые строки в массив объект localStorage:

localStorage.pushArrayItem('myArray','item one');
localStorage.pushArrayItem('myArray','item two');

пример использования - хранение объектов в массиве 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);

общие методы для работы с массивами:

.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
Комментарии (1)

Рекомендую использовать библиотеку абстракции для многих из описанных здесь, а также обеспечения лучшей совместимости. Много вариантов:

Комментарии (0)

Вы можете использовать localDataStorage прозрачно магазине в JavaScript типы данных (массив, булево, дата, с плавающей точкой, целое число, строка и объект). Она также обеспечивает легкий обфускация данных, автоматически сжимает струны, облегчает запрос по ключу (имени), а также запрос (ключ) значение, и помогает обеспечить сегментированные общего хранилища в пределах одного домена, предварив ключи.

[Отказ] Я автор утилиты [/отказ]

Примеры:

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

Как видите, примитивные ценности уважаются.

Комментарии (1)

Вы можете использовать ejson для хранения объектов в виде строк.

EJSON является расширением JSON для поддержки нескольких типов. Он поддерживает все в JSON-безопасных видов, а также:

Все EJSON сериализации также допустимый JSON. Например, объект с датой и двоичный буфер будет сериализовано в EJSON как:

{ на "Д" с: {" по$дата и": 1358205756553}, "и в”: {" в двоичном и quot$;: "по c3VyZS4=и"} }

Вот мой фантик localStorage с использованием ejson

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

Я добавил некоторые типы в мою оболочку, включая регулярные выражения и функции

Комментарии (0)

Другим вариантом было бы использовать существующий плагин.

Например persisto - проект с открытым исходным кодом, которая предоставляет простой интерфейс в localStorage/sessionStorage и автоматизирует сохранение полей формы (ввода, радио-кнопки и чекбоксы).

(Отказ от ответственности: я автор.)

Комментарии (1)

Я сделал еще одну минималистичную оболочку с всего лишь 20 строк кода, чтобы использовать его как следует:

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

Комментарии (0)

http://rhaboo.org это сахарный слой хранилище localStorage, которое позволяет писать такие вещи:

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

Это не'т использовать JSON.преобразовать в строки/разбор, потому что это будет неточное и медленное на больших объектах. Вместо этого, каждый стоимостью терминал имеет свои собственные записи хранилище localStorage.

Вы, наверное, можете догадаться, что я может что-то делать с rhaboo ;-)

Эдриан.

Комментарии (0)

Для машинопись пользователей, готовых установить и получить типизированные свойства:

/**
 * Silly wrapper to be able to type the storage keys
 */
export class TypedStorage {

    public removeItem(key: keyof T): void {
        localStorage.removeItem(key);
    }

    public getItem(key: K): T[K] | null {
        const data: string | null =  localStorage.getItem(key);
        return JSON.parse(data);
    }

    public setItem(key: K, value: T[K]): void {
        const data: string = JSON.stringify(value);
        localStorage.setItem(key, data);
    }
}

Пример использования:

// write an interface for the storage
interface MyStore {
   age: number,
   name: string,
   address: {city:string}
}

const storage: TypedStorage = new TypedStorage();

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");
Комментарии (0)

Чтобы сохранить объект, вы могли бы сделать буквы, которые вы можете использовать, чтобы получить объект из строки в объект (не имеет смысла). Например

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

Эта техника будет вызвать некоторые затруднения, если вы используете буквы, которые вы использовали, чтобы разделить объект, и это's также очень опытно.

Комментарии (0)

Я сделал то, что не'т сломать существующие объекты хранения, но и создает оболочку, так что вы можете делать то, что вы хотите. Результат нормальный объект, ни методы, с доступом как с любым объектом.

Что я сделал.

Если вы хотите 1 хранилище localStorage свойство магии:

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

Если вам нужно несколько:

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

Все, что вы делаете, чтобы "опора", или объектов внутри "склад" будет автоматически сохранен в хранилище localStorage`. Вы'вновь всегда играет с реального объекта, так что вы можете делать вещи, как это:

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

И каждый новый объект внутри отслеживаемый объект будет автоматически отслеживаться.

Очень большой недостаток: это зависит от объекта.наблюдать ()`, поэтому она имеет очень ограниченный браузер поддержка. И это вовсе'т выглядеть так, как она'll быть пришествие для Firefox или края в ближайшее время.

Комментарии (0)

Вот некоторые Расширенная версия код написал @danott

в Это'll тоже реализовать удалить значение из localStorage и показывает, как добавляет геттер и сеттер слой, поэтому вместо того, чтобы

хранилище localStorage.setitem класса(просмотр, правда)

вы можете написать

конфигурации.предварительный просмотр = истина

Ладно поехали:

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

Также вы можете лишить псевдонимы часть на <код>.привязать(...)</код>. Однако я просто положить его в, так как это's действительно хорошо, чтобы знать об этом. Я приняли меня часов, чтобы выяснить, почему простой <код>получайте = хранилище localStorage.метод getitem;</код> Дон't работа

Комментарии (0)