Przechowywanie obiektów w HTML5 localStorage

Chciałbym zapisać obiekt JavaScript w HTML5 localStorage, ale mój obiekt jest najwyraźniej konwertowany na ciąg znaków.

Mogę przechowywać i pobierać prymitywne typy JavaScript i tablice za pomocą localStorage, ale obiekty wydają się nie działać. A powinni?

Oto Mój kod:

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

Wyjście konsoli to

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

Wygląda na to, że metoda setItem konwertuje dane wejściowe na ciąg znaków przed ich zapisaniem.

Widzę takie zachowanie w Safari, Chrome, i Firefox, więc zakładam, że to moje nieporozumienie z HTML5 Web Storage spec, a nie specyficzny dla przeglądarki błąd lub ograniczenie.

Próbowałem zrozumieć strukturalny klon algorytm opisany w http://www.w3.org/TR/html5/infrastructure.html . nie do końca rozumiem co to mówi, ale może mój problem ma związek z tym, że właściwości mojego obiektu nie są wyliczalne (???)

Czy istnieje łatwe obejście?


Update: W3C ostatecznie zmienili zdanie na temat specyfikacji structured-clone i postanowili zmienić specyfikację, aby pasowała do implementacji. Zobacz https://www.w3.org/Bugs/Public/show_bug.cgi?id=12111 . więc to pytanie nie jest już w 100% poprawne, ale odpowiedzi nadal mogą być interesujące.

Author: Kristopher Johnson, 2010-01-06

22 answers

Patrząc na jabłko, Mozilla i Mozilla ponownie Dokumentacja, funkcjonalność wydaje się być ograniczona do obsługi tylko łańcuch klucz/wartość pary.

Obejście może być stringify obiekt przed zapisaniem go, a następnie przetworzy go po pobraniu:

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));
 3359
Author: Christian C. Salvadó,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-06-15 04:06:54

Drobna poprawa w wariancie :

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

Z powodu zwarcia, getObject() natychmiast zwróci null, Jeśli key nie znajduje się w magazynie. Nie wyrzuci również SyntaxError wyjątku, jeśli value jest "" (pusty łańcuch; JSON.parse() nie może tego obsłużyć).

 638
Author: Guria,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-28 19:40:05

Przydatne może okazać się rozszerzenie obiektu Storage za pomocą następujących przydatnych metod:

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

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

W ten sposób uzyskasz funkcjonalność, której naprawdę chciałeś, mimo że pod API obsługuje tylko ciągi znaków.

 225
Author: Justin Voskuhl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-06 04:42:47

Rozszerzenie obiektu pamięci masowej to niesamowite rozwiązanie. Dla mojego API utworzyłem fasadę dla localStorage, a następnie sprawdzałem, czy jest to obiekt, czy nie podczas ustawiania i pobierania.

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;
  }
}
 75
Author: Alex Grande,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-25 16:32:44

Stringify nie rozwiązuje wszystkich problemów

Wydaje się, że odpowiedzi tutaj nie obejmują wszystkich typów, które są możliwe w JavaScript, więc oto kilka krótkich przykładów, jak sobie z nimi poradzić poprawnie:

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

Nie polecam do przechowywania funkcji, ponieważ eval() to zło może prowadzić do problemów dotyczących bezpieczeństwa, optymalizacji i debugowania. Ogólnie rzecz biorąc, eval() nie powinno być nigdy używane w kodzie JavaScript.

Członkowie prywatni

Problem z używanie JSON.stringify() do przechowywania obiektów polega na tym, że funkcja ta nie może serializować prywatnych członków. Problem ten można rozwiązać poprzez nadpisanie metody .toString() (która jest wywoływana domyślnie podczas przechowywania danych w pamięci webowej):

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

Circular references

Innym problemem stringify nie można sobie poradzić są odniesienia okrągłe:

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

W tym przykładzie JSON.stringify() rzuci TypeError "konwertowanie struktury kołowej na JSON" . Jeśli przechowywanie odniesień okrągłych powinno być można użyć drugiego parametru 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;
    }
});

Jednak znalezienie skutecznego rozwiązania dla przechowywania odniesień kołowych w dużej mierze zależy od zadań, które należy rozwiązać, a odtwarzanie takich danych nie jest trywialne.

Istnieją już pewne pytania na tak radzenie sobie z tym problemem: Stringify (convert to JSON) obiekt JavaScript z okrągłym odniesieniem

 66
Author: maja,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:02:49

Istnieje świetna biblioteka, która owija wiele rozwiązań, dzięki czemu obsługuje nawet starsze przeglądarki o nazwie jStorage

Możesz ustawić obiekt

$.jStorage.set(key, value)

I łatwo go odzyskać

value = $.jStorage.get(key)
value = $.jStorage.get(key, "default value")
 53
Author: JProgrammer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-27 03:14:53

Teoretycznie możliwe jest przechowywanie obiektów z funkcjami:

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

Jednak serializacja/deserializacja funkcji jest zawodna, ponieważ jest zależna od implementacji .

 28
Author: aster_x,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-08 16:14:26

Dotarłem do tego postu po naciśnięciu na inny post, który został zamknięty jako DUPLIKAT tego-zatytułowany " Jak przechowywać tablicę w localstorage?'. Co jest w porządku, z tym, że żaden wątek nie daje pełnej odpowiedzi, jak można utrzymać tablicę w localStorage - jednak udało mi się stworzyć rozwiązanie oparte na informacjach zawartych w obu wątkach.

Więc jeśli ktoś inny chce mieć możliwość przesuwania/pop / przesuwania elementów w tablicy i chce, aby tablica była przechowywana w localStorage lub rzeczywiście sessionStorage, proszę bardzo:

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

Przykład użycia-przechowywanie prostych łańcuchów w tablicy localStorage:

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

Przykład użycia-przechowywanie obiektów w tablicy 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);

Popularne metody manipulowania tablicami:

.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
 26
Author: Andy Lorenz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-07 14:07:26

Zaleca korzystanie z biblioteki abstrakcji dla wielu funkcji omawianych tutaj, a także lepszą kompatybilność. Wiele opcji:

 14
Author: doublejosh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-01 23:13:24

Możesz użyć localDataStorage do przezroczystego przechowywania typów danych javascript (Array, Boolean, Date, Float, Integer, String i Object). Zapewnia również lekkie zaciemnianie danych, automatycznie kompresuje ciągi znaków, ułatwia zapytanie według klucza (nazwy), a także zapytanie według wartości (klucza) i pomaga wymusić segmentowaną współdzieloną pamięć masową w tej samej domenie przez prefiks kluczy.

[DISCLAIMER] jestem autorem narzędzia [/DISCLAIMER]

Przykłady:

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

As you widać, że prymitywne wartości są respektowane.

 7
Author: Mac,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-17 17:03:22

Inną opcją byłoby użycie istniejącej wtyczki.

Na przykład persisto jest projektem open source, który zapewnia łatwy interfejs do localStorage / sessionStorage i automatyzuje trwałość dla pól formularza (wprowadzania, przycisków radiowych i pól wyboru).

funkcje persisto

(Zastrzeżenie: jestem autorem.)

 4
Author: mar10,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-16 19:55:20

Możesz użyć ejson do przechowywania obiektów jako ciągów znaków.

EJSON jest rozszerzeniem JSON obsługującym więcej typów. Obsługuje wszystkie typy JSON-safe, a także:

Wszystkie SERIALIZACJE EJSON są również poprawny JSON. Na przykład obiekt z datą i buforem binarnym byłby serializowany w EJSON jako:

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

Oto mój wrapper localStorage przy użyciu ejson

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

Dodałem kilka typów do mojego wrappera, w tym wyrażenia regularne i funkcje

 4
Author: Tony Brix,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-28 15:41:31

Nie można przechowywać wartości klucza bez formatu String .

LocalStorage obsługuje tylko format ciągu znaków dla klucza / wartości.

Dlatego powinieneś przekonwertować swoje dane na string, niezależnie od tego, czy jest to Array czy Object .

Aby przechowywać dane w localStorage przede wszystkim stringify go za pomocą JSON.metoda stringify () .

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

Następnie, gdy chcesz odzyskać dane, musisz ponownie przeanalizować Łańcuch znaków, aby obiekt był obiektem.

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

Hope it pomaga.

 3
Author: Moshiur Rahman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-01-21 06:11:43

Zrobiłem kolejny minimalistyczny wrapper z tylko 20 linijkami kodu, aby umożliwić korzystanie z niego tak, jak powinien:

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
Author: zevero,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-28 10:30:11

Dla użytkowników maszynopisu chcących ustawić i uzyskać właściwości typed:

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

Przykład użycia :

// 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
Author: Flavien Volken,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-30 11:06:17

Https://github.com/adrianmay/rhaboo jest warstwą cukru localStorage, która pozwala pisać takie rzeczy:

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

Nie używa JSON.stringify / parse, ponieważ byłoby to niedokładne i powolne na dużych obiektach. Zamiast tego każda wartość terminala ma swój własny wpis localStorage.

Pewnie domyślasz się, że mogę mieć coś wspólnego z rhaboo.
 2
Author: Adrian May,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-05-08 07:59:18

Tutaj jakaś rozszerzona wersja kodu zamieszczonego przez @ danott

Zaimplementuje również usuń wartość z localstorage i pokazuje jak dodać warstwę Getter i Setter więc zamiast

localstorage.setItem(preview, true)

Możesz napisać

config.preview = true

Okay here were go:

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

Cóż, możesz rozebrać część aliasów .bind(...). Jednak po prostu włożyłem go, ponieważ naprawdę dobrze jest o tym wiedzieć. Poświęciłem wiele godzin, aby dowiedzieć się, dlaczego proste get = localStorage.getItem; nie działa

 1
Author: Nadu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-10 20:25:37

Zrobiłem coś, co nie łamie istniejących obiektów pamięci masowej, ale tworzy opakowanie, dzięki czemu możesz robić, co chcesz. Rezultatem jest normalny obiekt, bez metod, z dostępem jak każdy obiekt.

To, co zrobiłem.

Jeśli chcesz, aby 1 localStorage własność była magiczna:

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

Jeśli potrzebujesz kilku:

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

Wszystko, co robisz prop, lub obiekty wewnątrz storage zostanie automatycznie zapisany do localStorage. Zawsze bawisz się prawdziwym przedmiotem, więc możesz robić takie rzeczy:

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

I każdy nowy obiekt wewnątrz śledzony obiekt będzie automatycznie śledzony.

Bardzo duży minus: To zależy od {[7] } więc ma bardzo ograniczone wsparcie przeglądarki. I nie wygląda na to, że wkrótce pojawi się Firefox lub Edge.

 1
Author: Rudie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-28 20:39:41

Znalazłem sposób, aby to działało z obiektami, które mają cykliczne odniesienia.

Zróbmy obiekt z cyklicznymi odniesieniami.

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;

Nie możemy zrobić JSON.stringify tutaj, z powodu okrągłych odniesień.

okrężnica

LOCALSTORAGE.CYCLICJSON ma .stringify i .parse podobnie jak normalne JSON, ale działa z obiektami o okrągłych odniesieniach. ("Works" meaning parse(stringify (obj)) i obj są głęboko równe i mają identyczne zbiory 'wewnętrznych równości')

Ale możemy wystarczy użyć skrótów:

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

Wtedy, recovered będzie" to samo " do obj, w następującym znaczeniu:

[
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
]

Oto implementacja 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>
 1
Author: mathheadinclouds,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-11-12 19:38:38

Aby zapisać obiekt, możesz utworzyć literę, której możesz użyć, aby uzyskać obiekt z ciągu znaków do obiektu (może nie mieć sensu). Na przykład

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

Ta technika spowoduje pewne usterki, jeśli użyjesz litery, której użyłeś do podzielenia obiektu, i jest również bardzo eksperymentalna.

 0
Author: Seizefire,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-21 16:48:56

Circular References

W tej odpowiedzi skupiam się na obiektach tylko danych (bez funkcji itp.) z odniesieniami okrągłymi i rozwijają pomysły wymieniane przez maja i mathheadinclouds (używam jego przypadku testowego i mój kod jest kilka razy krótszy). Właściwie możemy użyć JSON.stringify with proper replacer - jeśli obiekt źródłowy zawiera wiele odniesień do jakiegoś obiektu, lub zawiera odniesienia kołowe, to odwołujemy się do niego specjalnym łańcuchem path-string (podobnym do JSONPath )

// JSON.strigify replacer for objects with circ ref
function refReplacer() {
  let m = new Map(), v= new Map(), init = null;

  return function(field, value) {
    let p= m.get(this) + (Array.isArray(this) ? `[${field}]` : '.' + field); 
    let isComplex= value===Object(value)
    
    if (isComplex) m.set(value, p);  
    
    let pp = v.get(value)||'';
    let path = p.replace(/undefined\.\.?/,'');
    let val = pp ? `#REF:${pp[0]=='[' ? '$':'$.'}${pp}` : value;
    
    !init ? (init=value) : (val===init ? val="#REF:$" : 0);
    if(!pp && isComplex) v.set(value, path);
   
    return val;
  }
}


// ---------------
// TEST
// ---------------

// gen obj with duplicate/circular references
let 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;
testObject = obj;

let json = JSON.stringify(testObject, refReplacer(), 4);

console.log("Test Object\n", testObject);
console.log("JSON with JSONpath references\n",json);

Parse such json with jsonpath-like references

// parse json with JSONpath references to object
function parseRefJSON(json) {
  let objToPath = new Map();
  let pathToObj = new Map();
  let o = JSON.parse(json);
  
  let traverse = (parent, field) => {
    let obj = parent;
    let path = '#REF:$';

    if (field !== undefined) {
      obj = parent[field];
      path = objToPath.get(parent) + (Array.isArray(parent) ? `[${field}]` : `${field?'.'+field:''}`);
    }

    objToPath.set(obj, path);
    pathToObj.set(path, obj);
    
    let ref = pathToObj.get(obj);
    if (ref) parent[field] = ref;

    for (let f in obj) if (obj === Object(obj)) traverse(obj, f);
  }
  
  traverse(o);
  return o;
}


// ---------------
// TEST 1
// ---------------

let json = `
{
    "L": {
        "L": {
            "v": "lorem",
            "uncle": {
                "L": {
                    "v": "dolor",
                    "uncle": "#REF:$.L"
                },
                "R": {
                    "L": {
                        "v": "sit",
                        "uncle": "#REF:$.L.L.uncle.L"
                    },
                    "R": {
                        "v": "amet",
                        "uncle": "#REF:$.L.L.uncle.L"
                    },
                    "uncle": "#REF:$.L"
                }
            }
        },
        "R": {
            "v": "ipsum",
            "uncle": "#REF:$.L.L.uncle"
        }
    },
    "R": "#REF:$.L.L.uncle"
}`;

let testObject = parseRefJSON(json);

console.log("Test Object\n", testObject);


// ---------------
// TEST 2
// ---------------

console.log('Tests from mathheadinclouds anser:');

let recovered = testObject;

let obj = { // original object
    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;

[
  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
].forEach(x=> console.log('test pass: '+x));

Aby załadować / zapisać wynik json do pamięci użyj następującego kodu

localStorage.myObject = JSON.stringify(testObject, refReplacer());  // save
testObject = parseRefJSON(localStorage.myObject);                   // load
 0
Author: Kamil Kiełczewski,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-08-04 15:50:02

LocalStorage.setItem ("użytkownik", JSON.stringify (user));

Następnie pobrać go ze sklepu i przekonwertować na obiekt ponownie:

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

Jeśli musimy usunąć wszystkie wpisy sklepu możemy po prostu zrobić:

LocalStorage.clear ();

 -2
Author: manasa woddeyar manu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-02-06 05:02:23