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


2512

Я хотел бы сохранить объект 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 , а не ошибка или ограничение для конкретного браузера.

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

Есть ли легкий обходной путь?


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


17
Кстати, вы правильно прочитали «алгоритм структурированного клонирования», просто спецификация была изменена с значений только для строки на это после того, как реализации вышли. Я отправил сообщение об ошибке bugzilla.mozilla.org/show_bug.cgi?id=538142 в mozilla для отслеживания этой проблемы.
Николай

2
Это похоже на работу для indexedDB ...
markasoftware

1
Как насчет хранения массива объектов в localStorage? Я сталкиваюсь с той же проблемой, что она преобразуется в строку.
Jayant Pareek

1
Вы могли бы вместо этого просто сериализовать массив? как хранить с JSON stringify, а затем снова анализировать при загрузке?
Брандито

1
Вы можете использовать localDataStorage для прозрачного хранения типов данных javascript (Array, Boolean, Date, Float, Integer, String и Object)
Mac

Ответы:


3174

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

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

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

160
обратите внимание, что любые метаданные будут удалены. вы просто получаете объект с парами ключ-значение, поэтому любой объект с поведением необходимо перестроить.
oligofren

5
@CMS может setItem выдать какое-то исключение, если данные превышают емкость?
Ашиш Неги

3
... применяется только к объектам с циклическими ссылками, JSON.stringify()расширяет ссылочный объект до его полного «содержимого» (неявно строкового) в объекте, который мы строковым образом. См .: stackoverflow.com/a/12659424/2044940
CodeManX

3
Проблема с этим подходом - это проблемы с производительностью, если вам приходится работать с большими массивами или объектами.
Марк

3
@oligofren true, но, как правильно предположил maja, eval () =>, это одно из полезных применений, вы можете легко получить код функции => сохранить его как строку и затем вернуть обратно eval () :)
jave.web

621

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

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()не может обработать это).


48
Я просто хочу быстро добавить использование, так как для меня это было не сразу понятно: var userObject = { userId: 24, name: 'Jack Bauer' }; и установить его. localStorage.setObject('user', userObject); Затем вернуть его из хранилища. userObject = localStorage.getObject('user'); Вы можете даже сохранить массив объектов, если хотите.
Зуаллауз

8
Это просто логическое выражение. Вторая часть оценивается только в том случае, если оставлено одно верно В этом случае результат целого выражения будет из правой части. Это популярный метод, основанный на том, как оцениваются логические выражения.
Гурия

4
Я не вижу смысла локальной переменной и оценки ярлыка здесь (за исключением незначительных улучшений производительности). Если keyне в локальном хранилище, window.localStorage.getItem(key)возвращается null- это не бросать «Неправомерный доступ» исключение - и JSON.parse(null)возвращается , nullкак хорошо - это не сгенерирует исключение либо, ни в Chromium 21 , ни в ES 5.1 раздела 15.12.2 , поскольку , String(null) === "null"которые могут интерпретироваться как литерал JSON .
PointedEars

6
Значения в Local Storage всегда являются примитивными строковыми значениями. Итак, что делает этот ярлык, когда кто-то хранит ""(пустую строку) раньше. Потому что он преобразует тип в falseи JSON.parse(""), что вызовет SyntaxErrorисключение, не вызывается.
PointedEars

2
Это не будет работать в IE8, поэтому вам лучше использовать функции в подтвержденном ответе, если вам нужно его поддержать.
Ezeke

220

Возможно, вам будет полезно расширить объект 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 поддерживаются только строки.


13
Хорошая идея - включить подход CMS в функцию, для этого просто нужны функциональные тесты: один для JSON.stringify, один для JSON.parse и один для проверки, может ли localStorage фактически устанавливать и извлекать объект. Модифицировать хост-объекты не очень хорошая идея; Я бы предпочел рассматривать это как отдельный метод, а не как localStorage.setObject.
Гаррет

4
Это getObject()вызовет SyntaxErrorисключение, если сохраненное значение есть "", потому что JSON.parse()не может обработать это. Смотрите подробности в моем редактировании ответа Гурии.
PointedEars

9
Просто мои два цента, но я уверен, что не стоит расширять объекты, предоставляемые поставщиком, вот так.
Сетен


73

Расширение объекта Storage является отличным решением. Для своего 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;
  }
}

1
Это было почти то, что мне было нужно. Просто нужно было добавить if (value == null) {return false} перед комментарием, иначе это привело к ошибке при проверке наличия ключа в localStorage.
Франческо Фраппорти

2
Это довольно круто на самом деле. Согласитесь с @FrancescoFrapporti, вам нужен if для нулевых значений. Я также добавил '|| value [0] == "[" 'test в случае, если в массиве есть.
rob_james

Хорошо, я отредактирую это. Хотя вам не нужна нулевая часть, но если вам нужно, я рекомендую три ===. Если вы используете JSHint или JSLint, вас предупредят против использования ==.
Алекс Гранде

3
И для не-ниндзя (как я), кто-то может привести пример использования этого ответа? Является ли это: data.set('username': 'ifedi', 'fullname': { firstname: 'Ifedi', lastname: 'Okonkwo'});?
Ифеди Оконкво,

Да, в самом деле! Когда я преодолел свое желание кормиться с ложечки, я взял код для проверки и получил его. Я думаю, что этот ответ хорош, потому что 1) В отличие от принятого ответа, для проверки определенных строк данных требуется время, и 2) В отличие от следующего, он не идет на расширение собственного объекта.
Ифеди Оконкво,

64

Stringify не решает всех проблем

Кажется, что ответы здесь не охватывают все типы, которые возможны в 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.stringify()для хранения объектов заключается в том, что эта функция не может сериализовать закрытые члены. Эта проблема может быть решена путем перезаписи .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);

Циркулярные ссылки

Другая проблема, с которой stringifyнельзя справиться, это циклические ссылки:

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

В этом примере JSON.stringify()будет выдано TypeError «Преобразование круговой структуры в JSON» . Если должно поддерживаться хранение циклических ссылок, можно использовать второй параметр JSON.stringify():

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

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

В SO уже есть вопрос, касающийся этой проблемы: Stringify (преобразовать в JSON) объект JavaScript с циклической ссылкой


2
Следовательно, и, разумеется, хранение данных в хранилище должно основываться исключительно на копиях простых данных. Не живые объекты.
Roko C. Buljan

51

Существует отличная библиотека, которая объединяет многие решения, поэтому она поддерживает даже старые браузеры, называемые jStorage.

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

$.jStorage.set(key, value)

И получить это легко

value = $.jStorage.get(key)
value = $.jStorage.get(key, "default value")

2
@SuperUberDuper jStorage требуется Prototype, MooTools или jQuery
JProgrammer

28

Теоретически возможно хранить объекты с функциями:

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

Однако сериализация / десериализация функций ненадежна, поскольку зависит от реализации .


1
Сериализация / десериализация функций ненадежна, поскольку зависит от реализации . Также вы хотите заменить c.f[k] = escape(a[k]); на Unicode-safe c.f[k] = encodeURIComponent(a[k]);и eval('b.' + k + ' = ' + unescape(data.f[k]));на b[k] = eval("(" + decodeURIComponent(data.f[k]) + ")");. Скобки требуются, потому что ваша функция, если она правильно сериализована, скорее всего, будет анонимной, что не является допустимым / оператором / (поэтому eval()) выдает SyntaxErrorисключение в противном случае).
PointedEars

И typeofэто оператор , не пишите это так, как если бы это была функция. Заменить typeof(a[k])на typeof a[k].
PointedEars

Помимо применения моих предложений и подчеркивания ненадежности подхода, я исправил следующие ошибки: 1. Не все переменные были объявлены. 2. for- inне был отфильтрован по собственным свойствам. 3. Стиль кода, включая ссылки, был противоречивым.
PointedEars

@PointedEars какая практическая разница это делает? спецификация говорит, что the use and placement of white space, line terminators, and semicolons within the representation String is implementation-dependent. я не вижу никаких функциональных различий.
Майкл

@Michael Часть, которую вы цитировали, начинается с Note *in particular* that …. Но спецификация возвращаемого значения начинается с An implementation-dependent representation of the function is returned. This representation has the syntax of a FunctionDeclaration.Возвращаемое значение может быть function foo () {}- при условии соответствующей реализации.
PointedEars

22

Я пришел к этому сообщению после того, как нажал на другое сообщение, которое было закрыто как дубликат этого - под названием «Как сохранить массив в localalstorage?». Это нормально, за исключением того, что ни один из потоков на самом деле не дает полного ответа о том, как вы можете поддерживать массив в 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

Это очень удобный набор методов для манипулирования массивами, хранящимися в localStorage или sessionStorage, и заслуживает гораздо большего уважения, чем привлекло. @ Энди Лоренц Спасибо, что нашли время, чтобы поделиться!
Velojet



6

Вы можете использовать localDataStorage для прозрачного хранения типов данных javascript (Array, Boolean, Date, Float, Integer, String и Object). Он также обеспечивает упрощенную обфускацию данных, автоматически сжимает строки, облегчает запрос по ключу (имени), а также по запросу (ключу) и помогает обеспечить сегментированное общее хранилище в том же домене с помощью префикса ключей.

[ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ] Я являюсь автором утилиты [/ ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ]

Примеры:

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
Это блестящий ресурс и именно то, что мне нужно. Я делаю Ionic-приложения с AngularJS, где мне нужно сохранить определенные объекты javascript в localStorage, и до этого момента я только что делал JSON.parse и JSON.stringify, и они работают, но это немного сложнее, чем возможность просто использовать утилиту, как эта. Я собираюсь попробовать это.
Нмута

4

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

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

постоянные особенности

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


Я все еще работаю с моим файлом readme, но моя версия не требует jQuery, как это, кажется, и есть, но она предоставляет альтернативу для работы с объектами jQuery. Я добавлю больше в ближайшем будущем, поскольку я буду больше с ним работать, чтобы помочь ему в дальнейшей обработке различных объектов jQuery и поддерживать такие вещи, как постоянные данные. Также +1 за попытку предоставить более простое решение! Кроме того, он использует все традиционные методы localStroage; exp: var lsh = new localStorageHelper(); lsh.setItem('bob', 'bill'); Также включает в себя события.
SpYk3HH

4

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

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

  • Дата (JavaScript Date)
  • Двоичный (JavaScript Uint8Arrayили результат EJSON.newBinary )
  • Пользовательские типы (см. EJSON.addType . Например, Mongo.ObjectID реализован таким образом.)

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

{
  "d": {"$date": 1358205756553},
  "b": {"$binary": "c3VyZS4="}
}

Вот моя оболочка localStorage с использованием ejson

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

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


2

Я сделал еще одну минималистичную оболочку, содержащую всего 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


2

Для пользователей Typescript, желающих установить и получить типизированные свойства:

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

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

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

    public setItem<K extends keyof T>(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<MyStore> = new TypedStorage<MyStore>();

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

2

https://github.com/adrianmay/rhaboo - слой сахара 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.stringify / parse, потому что это будет неточно и медленно для больших объектов. Вместо этого каждое значение терминала имеет свою собственную запись localStorage.

Вы можете догадаться, что я могу иметь какое-то отношение к rhaboo.


1

Вот некоторая расширенная версия кода, размещенная @danott

Также будет реализовано удаление значения из localstorage и показано, как добавить слой Getter и Setter вместо

localstorage.setItem(preview, true)

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

config.preview = true

Ладно, вот и пошли

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

Ну, вы можете лишить псевдонимы части .bind(...). Тем не менее, я просто вставил это, потому что об этом очень приятно знать. Я потратил несколько часов, чтобы понять, почему простой get = localStorage.getItem;не работает


1

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

То, что я сделал.

Если вы хотите, чтобы 1 localStorageсвойство было волшебным:

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

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

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

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

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

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

Очень большой недостаток: это зависит от того, Object.observe()поэтому он имеет очень ограниченную поддержку браузера. И не похоже, что это будет в ближайшее время для Firefox или Edge.


1

Вы не можете сохранить значение ключа без формата строки .

LocalStorage поддерживает только формат String для ключа / значения.

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

Чтобы сохранить данные в localStorage, в первую очередь зашифруйте их, используя метод JSON.stringify () .

var myObj = [{name:"test", time:"Date 2017-02-03T08:38:04.449Z"}];
localStorage.setItem('item', JSON.stringify(myObj));

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

var getObj = JSON.parse(localStorage.getItem('item'));

Надеюсь, поможет.


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

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


0

Я нашел способ заставить его работать с объектами, которые имеют циклические ссылки.

Давайте сделаем объект с циклическими ссылками.

obj = {
    L: {
        L: { v: 'lorem' },
        R: { v: 'ipsum' }
    },
    R: {
        L: { v: 'dolor' },
        R: {
            L: { v: 'sit' },
            R: { v: 'amet' }
        }
    }
}
obj.R.L.uncle = obj.L;
obj.R.R.uncle = obj.L;
obj.R.R.L.uncle = obj.R.L;
obj.R.R.R.uncle = obj.R.L;
obj.L.L.uncle = obj.R;
obj.L.R.uncle = obj.R;

Мы не можем сделать JSON.stringifyздесь, из-за циклических ссылок.

circularUncle

LOCALSTORAGE.CYCLICJSONимеет .stringifyи так .parseже, как обычноJSON , но работает с объектами с круговыми ссылками. («Работает» означает parse (stringify (obj)) и obj глубоко равны И имеют идентичные наборы «внутренних равенств»)

Но мы можем просто использовать ярлыки:

LOCALSTORAGE.setObject('latinUncles', obj)
recovered = LOCALSTORAGE.getObject('latinUncles')

Тогда recoveredбудет "то же самое" для obj, в следующем смысле:

[
obj.L.L.v === recovered.L.L.v,
obj.L.R.v === recovered.L.R.v,
obj.R.L.v === recovered.R.L.v,
obj.R.R.L.v === recovered.R.R.L.v,
obj.R.R.R.v === recovered.R.R.R.v,
obj.R.L.uncle === obj.L,
obj.R.R.uncle === obj.L,
obj.R.R.L.uncle === obj.R.L,
obj.R.R.R.uncle === obj.R.L,
obj.L.L.uncle === obj.R,
obj.L.R.uncle === obj.R,
recovered.R.L.uncle === recovered.L,
recovered.R.R.uncle === recovered.L,
recovered.R.R.L.uncle === recovered.R.L,
recovered.R.R.R.uncle === recovered.R.L,
recovered.L.L.uncle === recovered.R,
recovered.L.R.uncle === recovered.R
]

Вот реализация LOCALSTORAGE

LOCALSTORAGE = (function(){
  "use strict";
  var ignore = [Boolean, Date, Number, RegExp, String];
  function primitive(item){
    if (typeof item === 'object'){
      if (item === null) { return true; }
      for (var i=0; i<ignore.length; i++){
        if (item instanceof ignore[i]) { return true; }
      }
      return false;
    } else {
      return true;
    }
  }
  function infant(value){
    return Array.isArray(value) ? [] : {};
  }
  function decycleIntoForest(object, replacer) {
    if (typeof replacer !== 'function'){
      replacer = function(x){ return x; }
    }
    object = replacer(object);
    if (primitive(object)) return object;
    var objects = [object];
    var forest  = [infant(object)];
    var bucket  = new WeakMap(); // bucket = inverse of objects 
    bucket.set(object, 0);    
    function addToBucket(obj){
      var result = objects.length;
      objects.push(obj);
      bucket.set(obj, result);
      return result;
    }
    function isInBucket(obj){ return bucket.has(obj); }
    function processNode(source, target){
      Object.keys(source).forEach(function(key){
        var value = replacer(source[key]);
        if (primitive(value)){
          target[key] = {value: value};
        } else {
          var ptr;
          if (isInBucket(value)){
            ptr = bucket.get(value);
          } else {
            ptr = addToBucket(value);
            var newTree = infant(value);
            forest.push(newTree);
            processNode(value, newTree);
          }
          target[key] = {pointer: ptr};
        }
      });
    }
    processNode(object, forest[0]);
    return forest;
  };
  function deForestIntoCycle(forest) {
    var objects = [];
    var objectRequested = [];
    var todo = [];
    function processTree(idx) {
      if (idx in objects) return objects[idx];
      if (objectRequested[idx]) return null;
      objectRequested[idx] = true;
      var tree = forest[idx];
      var node = Array.isArray(tree) ? [] : {};
      for (var key in tree) {
        var o = tree[key];
        if ('pointer' in o) {
          var ptr = o.pointer;
          var value = processTree(ptr);
          if (value === null) {
            todo.push({
              node: node,
              key: key,
              idx: ptr
            });
          } else {
            node[key] = value;
          }
        } else {
          if ('value' in o) {
            node[key] = o.value;
          } else {
            throw new Error('unexpected')
          }
        }
      }
      objects[idx] = node;
      return node;
    }
    var result = processTree(0);
    for (var i = 0; i < todo.length; i++) {
      var item = todo[i];
      item.node[item.key] = objects[item.idx];
    }
    return result;
  };
  var console = {
    log: function(x){
      var the = document.getElementById('the');
      the.textContent = the.textContent + '\n' + x;
	},
	delimiter: function(){
      var the = document.getElementById('the');
      the.textContent = the.textContent +
		'\n*******************************************';
	}
  }
  function logCyclicObjectToConsole(root) {
    var cycleFree = decycleIntoForest(root);
    var shown = cycleFree.map(function(tree, idx) {
      return false;
    });
    var indentIncrement = 4;
    function showItem(nodeSlot, indent, label) {
      var leadingSpaces = ' '.repeat(indent);
      var leadingSpacesPlus = ' '.repeat(indent + indentIncrement);
      if (shown[nodeSlot]) {
        console.log(leadingSpaces + label + ' ... see above (object #' + nodeSlot + ')');
      } else {
        console.log(leadingSpaces + label + ' object#' + nodeSlot);
        var tree = cycleFree[nodeSlot];
        shown[nodeSlot] = true;
        Object.keys(tree).forEach(function(key) {
          var entry = tree[key];
          if ('value' in entry) {
            console.log(leadingSpacesPlus + key + ": " + entry.value);
          } else {
            if ('pointer' in entry) {
              showItem(entry.pointer, indent + indentIncrement, key);
            }
          }
        });
      }
    }
	console.delimiter();
    showItem(0, 0, 'root');
  };
  function stringify(obj){
    return JSON.stringify(decycleIntoForest(obj));
  }
  function parse(str){
    return deForestIntoCycle(JSON.parse(str));
  }
  var CYCLICJSON = {
    decycleIntoForest: decycleIntoForest,
    deForestIntoCycle : deForestIntoCycle,
    logCyclicObjectToConsole: logCyclicObjectToConsole,
    stringify : stringify,
    parse : parse
  }
  function setObject(name, object){
    var str = stringify(object);
    localStorage.setItem(name, str);
  }
  function getObject(name){
    var str = localStorage.getItem(name);
    if (str===null) return null;
    return parse(str);
  }
  return {
    CYCLICJSON : CYCLICJSON,
    setObject  : setObject,
    getObject  : getObject
  }
})();
obj = {
	L: {
		L: { v: 'lorem' },
		R: { v: 'ipsum' }
	},
	R: {
		L: { v: 'dolor' },
		R: {
			L: { v: 'sit' },
			R: { v: 'amet' }
		}
	}
}
obj.R.L.uncle = obj.L;
obj.R.R.uncle = obj.L;
obj.R.R.L.uncle = obj.R.L;
obj.R.R.R.uncle = obj.R.L;
obj.L.L.uncle = obj.R;
obj.L.R.uncle = obj.R;

// LOCALSTORAGE.setObject('latinUncles', obj)
// recovered = LOCALSTORAGE.getObject('latinUncles')
// localStorage not available inside fiddle ):
LOCALSTORAGE.CYCLICJSON.logCyclicObjectToConsole(obj)
putIntoLS = LOCALSTORAGE.CYCLICJSON.stringify(obj);
recovered = LOCALSTORAGE.CYCLICJSON.parse(putIntoLS);
LOCALSTORAGE.CYCLICJSON.logCyclicObjectToConsole(recovered);

var the = document.getElementById('the');
the.textContent = the.textContent + '\n\n' +
JSON.stringify(
[
obj.L.L.v === recovered.L.L.v,
obj.L.R.v === recovered.L.R.v,
obj.R.L.v === recovered.R.L.v,
obj.R.R.L.v === recovered.R.R.L.v,
obj.R.R.R.v === recovered.R.R.R.v,
obj.R.L.uncle === obj.L,
obj.R.R.uncle === obj.L,
obj.R.R.L.uncle === obj.R.L,
obj.R.R.R.uncle === obj.R.L,
obj.L.L.uncle === obj.R,
obj.L.R.uncle === obj.R,
recovered.R.L.uncle === recovered.L,
recovered.R.R.uncle === recovered.L,
recovered.R.R.L.uncle === recovered.R.L,
recovered.R.R.R.uncle === recovered.R.L,
recovered.L.L.uncle === recovered.R,
recovered.L.R.uncle === recovered.R
]
)
<pre id='the'></pre>


-2

localStorage.setItem ('user', JSON.stringify (user));

Затем, чтобы извлечь его из магазина и снова преобразовать в объект:

var user = JSON.parse (localStorage.getItem ('user'));

Если нам нужно удалить все записи магазина, мы можем просто сделать:

localStorage.clear ();


3
Это 10-летний вопрос. Как вы думаете, ваш ответ добавляет что-то еще, что не охвачено другими ответами?
Кристофер Джонсон
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.