Зберігання об’єктів у HTML5 localStorage


2510

Я хотів би зберігати об’єкт 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 . Тож це питання вже не стовідсоткове, але відповіді все ж можуть зацікавити.


17
До речі, ваше читання "структурованого алгоритму клонування" є правильним, це просто те, що специфікація була змінена з лише рядкових значень на це після того, як були внесені програми. Я подав помилку bugzilla.mozilla.org/show_bug.cgi?id=538142 з мозіллою, щоб відстежити цю проблему.
Миколай

2
Це здається роботою для indexedDB ...
markasoftware

1
Як щодо збереження масиву об’єктів у localStorage? Я зіткнувся з тією ж проблемою, що вона перетворюється на рядок.
Jayant Pareek

1
ви могли б замість цього просто серіалізувати масив? як магазин з JSON stringify, потім знову розібратися при завантаженні?
Брандіто

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

Відповіді:


3169

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

Обхідним способом може бути розшарування об'єкта перед його збереженням, а пізніше його аналіз під час його отримання:

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

159
зауважте, що метадані будуть видалені. ви просто отримуєте об'єкт з парами ключ-значення, тому будь-який об’єкт з поведінкою потрібно перебудовувати.
олігофрен

5
@CMS може setItem кинути якийсь виняток, якщо дані перевищують ємність?
Ашиш Негі

3
... застосовується лише до об'єктів з циркулярними посиланнями, JSON.stringify()розширює посилається об'єкт на його повний "вміст" (неявно впорядкований) в об'єкті, який ми раціоналізуємо. Див: stackoverflow.com/a/12659424/2044940
CodeManX

3
Проблема такого підходу - це питання продуктивності, якщо вам доведеться обробляти великі масиви або об'єкти.
Марк

3
@oligofren вірно, але як мая правильно запропонувала 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'); Ви можете навіть зберігати масив об’єктів, якщо хочете.
zuallauz

8
Це просто булевий вираз. Друга частина оцінюється лише в тому випадку, якщо одна з них правдива. У такому випадку результат цілого вираження буде з правої частини. Це популярна техніка, заснована на тому, як оцінюються булеві вирази.
Гурія

4
Я не бачу сенсу локальної змінної та оцінки ярлика тут (незначні покращення продуктивності вбік). Якщо keyнемає в Local Storage, window.localStorage.getItem(key)повертається null- він не кидає виняток "Незаконний доступ" - і JSON.parse(null)повертається nullтакож - він не викидає винятку, ні в Chromium 21, ні в розділі 15.12.2 ES 5.1 , тому String(null) === "null"що який може інтерпретувати як буквальний JSON .
PointedEars

6
Значення в Local Storage - це завжди примітивні рядкові значення. Отож, з чим справляється ця оцінка ярликів, коли хтось зберігав ""(порожній рядок) раніше. Тому що він перетворює тип на, falseі JSON.parse("")який би кинув SyntaxErrorвиняток, не називається.
PointedEars

2
Це не працює в IE8, тому вам краще використовувати функції у підтвердженій відповіді, якщо вам потрібно його підтримати.
Єзеке

220

Вам може бути корисно розширити об’єкт зберігання цими зручними методами:

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.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, тобі нульові значення потрібні, якщо там. Я також додав '|| значення [0] == "[" 'тест на випадок, якщо там є масив.
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
Отже, і зайве говорити, що зберігання даних у Storage має базуватися на єдиній передумові копій простих даних. Не живі об’єкти.
Roko C. Buljan

51

Є чудова бібліотека, яка охоплює багато рішень, тому вона навіть підтримує старі браузери, які називаються jStorage

Ви можете встановити об'єкт

$.jStorage.set(key, value)

І витягніть це легко

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

2
@SuperUberDuper jStorage вимагає прототипу, 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 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 () {}- якщо припустити, що відповідає .
Вказане вуха

22

Я прийшов на цю посаду після того, як потрапив на інший пост, закритий як дублікат цього, - під назвою "як зберігати масив у локальному сховищі?". Що добре, за винятком того, що жоден потік насправді не дає повну відповідь щодо того, як можна підтримувати масив у 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, і заслуговує на набагато більше кредитів, ніж приваблює. @Andy Lorenz Дякуємо, що знайшли час поділитися!
Велоджет


14

Рекомендуйте використовувати бібліотеку абстракцій для багатьох функцій, про які йдеться тут, а також кращої сумісності. Безліч варіантів:


6

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

[DISCLAIMER] Я автор утиліти [/ DISCLAIMER]

Приклади:

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 і автоматизує стійкість для полів форми (введення, перемикачі та прапорці).

особливості persisto

(Відмова: Я - автор.)


Я все ще працюю над моїм readme, але моя версія не вимагає jQuery, як видається persisto, але це дає альтернативу для роботи з елементами jQuery Elements. Я ще більше додаю, оскільки більше працюю з ним, щоб допомогти йому додатково обробляти різні об’єкти 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 - це місцевий рівень цукрового шару, який дозволяє писати такі речі:

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, тому що це буде неточно та повільно для великих об'єктів. Натомість у кожного значення терміналу є своя запис локального зберігання.

Ви, напевно, можете здогадатися, що я можу мати щось спільне з рабу.


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

Ви не можете зберігати значення ключа без String Format.

LocalStorage підтримує лише String-формат для ключа / значення.

Ось чому ви повинні перетворити свої дані в рядок незалежно від масиву чи об’єкта .

Для зберігання даних у localStorage насамперед їх поглинають за допомогою методу JSON.stringify () .

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

Потім, коли ви хочете отримати дані, вам потрібно знову проаналізувати String to Object.

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це через кругові посилання.

круговий дядько

LOCALSTORAGE.CYCLICJSONмає .stringifyі .parseподібно до звичайних JSON, але працює з об'єктами з круговими посиланнями. ("Роботи", що означають розбір (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 ('користувач', JSON.stringify (користувач));

Потім витягніть його з магазину та знову перетворіть на об’єкт:

var user = JSON.parse (localStorage.getItem ('користувач'));

Якщо нам потрібно видалити всі записи магазину, ми можемо просто зробити:

localStorage.clear ();


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