Przechowywanie obiektów w HTML5 localStorage

2510

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

Mogę przechowywać i pobierać prymitywne typy i tablice JavaScript za pomocą localStorage, ale obiekty nie działają. 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);

Dane wyjściowe konsoli to

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

Wydaje mi się, że ta setItemmetoda konwertuje dane wejściowe na ciąg znaków przed ich zapisaniem.

Widzę to zachowanie w Safari, Chrome i Firefox, więc zakładam, że nie rozumiem specyfikacji HTML5 Web Storage , a nie błędu lub ograniczenia specyficznego dla przeglądarki.

Próbowałem zrozumieć algorytm ustrukturyzowanego klonowania 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 dotyczy tego, że właściwości mojego obiektu nie są policzalne (???)

Czy istnieje łatwe obejście?


Aktualizacja: W3C ostatecznie zmieniło zdanie na temat specyfikacji klonowania strukturalnego i zdecydowało się zmienić specyfikację, aby pasowała do implementacji. Zobacz https://www.w3.org/Bugs/Public/show_bug.cgi?id=12111 . To pytanie nie jest już w 100% prawidłowe, ale odpowiedzi mogą być interesujące.

Kristopher Johnson
źródło
17
BTW, twój odczyt „strukturalnego algorytmu klonowania” jest poprawny, po prostu specyfikacja została zmieniona z wartości tylko na ciąg znaków na to po wydaniu implementacji. Złożyłem błąd bugzilla.mozilla.org/show_bug.cgi?id=538142 w Mozilli, aby śledzić ten problem.
Nickolay,
2
To wygląda na pracę dla indexedDB ...
markasoftware
1
Co powiesz na przechowywanie tablicy obiektów w localStorage? Mam do czynienia z tym samym problemem, który jest konwertowany na ciąg znaków.
Jayant Pareek
1
czy zamiast tego możesz po prostu serializować tablicę? jak sklep z JSON stringify, a następnie parsować ponownie po załadowaniu?
Brandito,
1
Za pomocą localDataStorage można w przejrzysty sposób przechowywać typy danych javascript (Array, Boolean, Date, Float, Integer, String and Object)
Mac

Odpowiedzi:

3169

Patrząc ponownie na dokumentację Apple , Mozilla i Mozilla , wydaje się, że funkcjonalność jest ograniczona do obsługi tylko par ciągów klucz / wartość.

Obejściem może być sprecyzowanie obiektu przed zapisaniem go, a następnie jego przeanalizowanie po odzyskaniu:

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));
CMS
źródło
159
zauważ, że wszelkie metadane zostaną usunięte. po prostu dostajesz obiekt z parami klucz-wartość, więc każdy obiekt z zachowaniem musi zostać odbudowany.
oligofren
5
@CMS może setItem zgłosić wyjątek, jeśli dane przekraczają pojemność?
Ashish Negi
3
... dotyczy tylko obiektów z odwołaniami cyklicznymi, JSON.stringify()rozwija obiekt, do którego się odwołuje, do pełnej „treści” (niejawnie zeszeregowanej) w obiekcie, który tworzymy. Zobacz: stackoverflow.com/a/12659424/2044940
CodeManX
3
Problemem w tym podejściu są problemy z wydajnością, jeśli trzeba obsługiwać duże tablice lub obiekty.
Mark
3
@oligofren prawda, ale jak maja poprawnie zasugerowała eval () =>, jest to jedno z dobrych zastosowań, możesz łatwo pobrać kod funkcji => zapisać go jako ciąg znaków, a następnie eval () z powrotem :)
jave.web
621

Niewielka 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 oceny zwarcia , getObject()będzie natychmiast wrócić null, jeśli keynie ma w magazynie. Nie wyrzuci również SyntaxErrorwyjątku, jeśli valuejest ""(pusty ciąg; JSON.parse()nie może tego obsłużyć).

Guria
źródło
48
Chcę tylko szybko dodać użycie, ponieważ nie było to dla mnie od razu jasne: var userObject = { userId: 24, name: 'Jack Bauer' }; I ustawić je, a localStorage.setObject('user', userObject); następnie odzyskać z pamięci. userObject = localStorage.getObject('user'); Możesz nawet zapisać tablicę obiektów, jeśli chcesz.
zuallauz,
8
To jest po prostu wyrażenie boolowskie. Druga część jest oceniana tylko wtedy, gdy pozostała jest prawdą. W takim przypadku wynik całego wyrażenia będzie z prawej strony. Jest to popularna technika oparta na sposobie oceny wyrażeń boolowskich.
Guria,
4
Nie widzę tutaj sensu zmiennej lokalnej ani oceny skrótu (pomijając niewielkie ulepszenia wydajności). Jeśli keynie jest w pamięci lokalnej, window.localStorage.getItem(key)powroty null- to jednak nie rzucać się „nielegalnego dostępu” wyjątek - i JSON.parse(null)powraca nulljak dobrze - to jednak nie wyjątek albo, ani w Chromium 21 ani za ES 5.1 sekcji 15.12.2 , ponieważ String(null) === "null", które mogą być interpretowane jako literał JSON .
PointedEars,
6
Wartości w magazynie lokalnym są zawsze pierwotnymi wartościami ciągu. Zatem ta ocena skrótu obsługuje, gdy wcześniej ktoś zapisał ""(pusty ciąg). Ponieważ konwertuje typ na falsei JSON.parse(""), co spowodowałoby SyntaxErrorwyjątek, nie jest wywoływany.
PointedEars,
2
To nie działa w IE8, więc lepiej jest użyć funkcji w potwierdzonej odpowiedzi, jeśli chcesz ją obsługiwać.
Ezeche
220

Przydatne może być rozszerzenie obiektu Storage za pomocą tych 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 otrzymujesz funkcjonalność, którą naprawdę chciałeś, mimo że pod API obsługuje tylko łańcuchy.

Justin Voskuhl
źródło
13
Zawijanie podejścia CMS do funkcji jest dobrym pomysłem, wymaga tylko testów funkcji: jeden dla JSON.stringify, jeden dla JSON.parse, a drugi do sprawdzenia, czy localStorage może faktycznie ustawić i pobrać obiekt. Modyfikowanie obiektów hosta nie jest dobrym pomysłem; Wolę postrzegać to jako osobną metodę, a nie jako localStorage.setObject.
Garrett
4
Spowoduje getObject()to zgłoszenie SyntaxErrorwyjątku, jeśli przechowywana jest wartość "", ponieważ JSON.parse()nie może tego obsłużyć. Zobacz moją edycję odpowiedzi Gurii, aby uzyskać szczegółowe informacje.
PointedEars,
9
Tylko moje dwa centy, ale jestem pewien, że nie jest dobrym pomysłem rozszerzanie obiektów dostarczanych przez dostawcę w ten sposób.
Sethen
73

Rozszerzenie obiektu Storage to niesamowite rozwiązanie. Dla mojego interfejsu API utworzyłem fasadę dla localStorage, a następnie sprawdzam, 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;
  }
}
Alex Grande
źródło
1
To było prawie dokładnie to, czego potrzebowałem. Po prostu musiałem dodać if (wartość == null) {return false} przed komentarzem, w przeciwnym razie spowodowałoby to błąd podczas sprawdzania istnienia klucza w localStorage.
Francesco Frapporti
2
To jest całkiem fajne. Zgadzam się z @FrancescoFrapporti, jeśli potrzebujesz wartości if dla wartości null. Dodałem także „|| wartość [0] == "[" "test na wypadek, gdyby była tam tablica.
rob_james
Dobra uwaga, zmienię to. Chociaż nie potrzebujesz części zerowej, ale jeśli tak, polecam trzy ===. Jeśli użyjesz JSHint lub JSLint, zostaniesz ostrzeżony przed użyciem ==.
Alex Grande,
3
A w przypadku osób niebędących ninja (takich jak ja), czy ktoś mógłby podać przykład użycia tej odpowiedzi? To jest: data.set('username': 'ifedi', 'fullname': { firstname: 'Ifedi', lastname: 'Okonkwo'});?
Ifedi Okonkwo
W rzeczy samej! Kiedy przezwyciężyłem pragnienie karmienia łyżką, wziąłem kod do przetestowania i dostałem go. Myślę, że ta odpowiedź jest świetna, ponieważ 1) W przeciwieństwie do przyjętej odpowiedzi, sprawdzenie danych ciągu zajmuje trochę czasu, i 2) W przeciwieństwie do następnej, nie rozszerza obiektu natywnego.
Ifedi Okonkwo
64

Stringify nie rozwiązuje wszystkich problemów

Wygląda na to, że odpowiedzi tutaj nie obejmują wszystkich typów możliwych w JavaScript, więc oto kilka krótkich przykładów, jak poprawnie sobie z nimi poradzić:

//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 przechowywania funkcji, ponieważ eval()zło może prowadzić do problemów związanych z bezpieczeństwem, optymalizacją i debugowaniem. Zasadniczo eval()nigdy nie należy go używać w kodzie JavaScript.

Członkowie prywatni

Problem z używaniem JSON.stringify()do przechowywania obiektów polega na tym, że ta funkcja nie może serializować prywatnych członków. Ten problem można rozwiązać, zastępując .toString()metodę (która jest wywoływana niejawnie podczas przechowywania danych w pamięci internetowej):

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

Referencje kołowe

Innym problemem, z którym stringifynie można sobie poradzić, są odwołania cykliczne:

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

W tym przykładzie JSON.stringify()wyrzuci TypeError „Konwertowanie okrągłej struktury na JSON” . Jeśli powinno być obsługiwane przechowywanie odniesień cyklicznych, 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 do przechowywania cyklicznych odniesień w dużej mierze zależy od zadań, które należy rozwiązać, a przywrócenie takich danych nie jest również trywialne.

Istnieją już pytania dotyczące SO dotyczące tego problemu: Stringify (konwersja do JSON) obiekt JavaScript z referencją cykliczną

maja
źródło
2
W związku z tym - i nie trzeba dodawać - przechowywanie danych w pamięci powinno opierać się wyłącznie na kopiach prostych danych. Nie żywe obiekty.
Roko C. Buljan
51

Istnieje świetna biblioteka, która zawiera wiele rozwiązań, więc obsługuje nawet starsze przeglądarki o nazwie jStorage

Możesz ustawić obiekt

$.jStorage.set(key, value)

I łatwo to odzyskać

value = $.jStorage.get(key)
value = $.jStorage.get(key, "default value")
JProgrammer
źródło
2
@SuperUberDuper jStorage wymaga prototypu, MooTools lub jQuery
JProgrammer
28

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ż zależy od implementacji .

aster_x
źródło
1
Serializacja / deserializacja funkcji jest zawodna, ponieważ zależy od implementacji . Również chcesz zamienić c.f[k] = escape(a[k]); z Unicode bezpieczny c.f[k] = encodeURIComponent(a[k]);i eval('b.' + k + ' = ' + unescape(data.f[k]));z b[k] = eval("(" + decodeURIComponent(data.f[k]) + ")");. Nawiasy są wymagane, ponieważ twoja funkcja, jeśli zostanie poprawnie zserializowana, prawdopodobnie będzie anonimowa, co nie jest tak, jak jest poprawne / Instrukcja / (więc eval()) w SyntaxErrorprzeciwnym razie zgłasza wyjątek).
PointedEars,
I typeofjest operatorem , nie pisz go tak, jakby to była funkcja. Wymień typeof(a[k])się typeof a[k].
PointedEars,
Oprócz zastosowania moich sugestii i podkreślenia niewiarygodności podejścia naprawiłem następujące błędy: 1. Nie wszystkie zmienne zostały zadeklarowane. 2. for- innie został przefiltrowany dla własnych właściwości. 3. Styl kodu, w tym odniesienia, był niespójny.
PointedEars,
@PointedEars, jaka to praktyczna różnica? specyfikacja mówi the use and placement of white space, line terminators, and semicolons within the representation String is implementation-dependent. , że nie widzę żadnych różnic funkcjonalnych.
Michael
@ Michael Część, którą zacytowałeś, zaczyna się od Note *in particular* that …. Ale specyfikacja wartości zwracanej zaczyna się od An implementation-dependent representation of the function is returned. This representation has the syntax of a FunctionDeclaration.Wartość zwracana może być function foo () {}- przy założeniu zgodnej implementacji.
PointedEars,
22

Doszedłem do tego postu po trafieniu na inny post, który został zamknięty jako duplikat tego - zatytułowany „jak przechowywać tablicę w localstorage?”. Co jest w porządku, z wyjątkiem tego, że żaden wątek nie zapewnia pełnej odpowiedzi na temat tego, w jaki sposób można utrzymywać tablicę w localStorage - udało mi się jednak opracować rozwiązanie na podstawie informacji zawartych w obu wątkach.

Więc jeśli ktokolwiek chce móc pchać / pop / przesuwać elementy w tablicy i chce, aby tablica była przechowywana w localStorage lub faktycznie sessionStorage, proszę:

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 ciągó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
Andy Lorenz
źródło
Jest to bardzo przydatny zestaw metod do manipulowania tablicami przechowywanymi w localStorage lub sessionStorage i zasługuje na znacznie więcej kredytu, niż jest przyciągany. @Andy Lorenz Dziękujemy za poświęcenie czasu na udostępnienie!
Velojet,
6

Za pomocą localDataStorage można w przejrzysty sposób przechowywać typy danych javascript (Array, Boolean, Date, Float, Integer, String i Object). Zapewnia również lekkie zaciemnianie danych, automatycznie kompresuje ciągi, ułatwia zapytania według klucza (nazwy), a także zapytania według (klucza) wartości, a także pomaga wymuszać dzielone dzielone miejsce do magazynowania w tej samej domenie przez prefiksowanie kluczy.

[OŚWIADCZENIE] Jestem autorem narzędzia [/ OŚWIADCZENIE]

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

Jak widać, prymitywne wartości są przestrzegane.

Prochowiec
źródło
1
To genialny zasób i właśnie tego potrzebuję. Robię aplikacje jonowe z AngularJS, w których muszę zapisywać pewne obiekty javascript w localStorage i do tej pory właśnie robiłem JSON.parse i JSON.stringify i działają, ale jest to trochę bardziej kłopotliwe niż możliwość po prostu użyć narzędzia takiego jak ten. Spróbuję tego.
Nmuta
4

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

Na przykład persisto to projekt typu open source, który zapewnia łatwy interfejs do localStorage / sessionStorage i automatyzuje trwałość pól formularzy (wprowadzania, przycisków opcji i pól wyboru).

cechy trwałe

(Uwaga: Jestem autorem).

mar10
źródło
Nadal pracuję nad moim plikiem Readme, ale moja wersja nie wymaga jQuery, ponieważ wydaje się, że trwa, ale stanowi alternatywę dla obsługi obiektów jQuery Objects. Dodam więcej w najbliższej przyszłości, ponieważ będę z nim więcej pracować, aby pomóc mu dalej obsługiwać różne obiekty jQuery i utrzymywać takie rzeczy, jak trwałe dane. Również +1 za próbę zapewnienia prostszego rozwiązania! Ponadto wykorzystuje wszystkie tradycyjne metody localStroage; exp: var lsh = new localStorageHelper(); lsh.setItem('bob', 'bill'); Obejmuje również zdarzenia.
SpYk3HH,
4

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

EJSON to rozszerzenie JSON do obsługi większej liczby typów. Obsługuje wszystkie typy bezpieczne dla JSON, a także:

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

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

Oto moje opakowanie localStorage za pomocą ejson

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

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

Tony Brix
źródło
2

Zrobiłem kolejne minimalistyczne opakowanie z tylko 20 liniami kodu, aby umożliwić korzystanie z niego tak, jak powinno:

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

zevero
źródło
2

Dla użytkowników maszynopisu, którzy chcą ustawić i uzyskać właściwości pisania:

/**
 * 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ładowe użycie :

// 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");
Flavien Volken
źródło
2

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 / parsować, ponieważ byłoby to niedokładne i wolne dla dużych obiektów. Zamiast tego każda wartość terminala ma własny wpis localStorage.

Prawdopodobnie można się domyślić, że mógłbym mieć coś wspólnego z rababem.

Adrian May
źródło
1

Oto rozbudowana wersja kodu opublikowana przez @danott

Zaimplementuje również wartość usuwania z pamięci lokalnej i pokaże, jak dodać warstwę Gettera i Settera zamiast

localstorage.setItem(preview, true)

Możesz pisać

config.preview = true

Okej, proszę bardzo:

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

Możesz usunąć część aliasów .bind(...). Jednak po prostu to włożyłem, ponieważ naprawdę dobrze o tym wiedzieć. Zajęło mi godziny, aby dowiedzieć się, dlaczego prosty get = localStorage.getItem;nie działa

Nadu
źródło
1

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

Rzecz, którą zrobiłem.

Jeśli chcesz, aby 1 localStoragewłaściwość była magiczna:

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

Jeśli potrzebujesz kilku:

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

Wszystko, co zrobisz prop, lub przedmioty w środku storage zostaną automatycznie zapisane 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 przedmiot w środku śledzonym obiekcie będzie automatycznie śledzony.

Bardzo duży minus: zależy, Object.observe()więc ma bardzo ograniczone wsparcie przeglądarki. I nie wygląda na to, że wkrótce pojawi się w przeglądarce Firefox lub Edge.

Rudie
źródło
1

Nie można zapisać wartości klucza bez String Format.

Lokalny magazyn obsługuje tylko format ciągu dla klucza / wartości.

Dlatego należy przekonwertować dane na ciąg cokolwiek to jest Array lub obiektu .

Aby przechowywać dane w localStorage, należy przede wszystkim skreślić je za pomocą metody JSON.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ć ciąg znaków do obiektu.

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

Mam nadzieję, że to pomoże.

Moshiur Rahman
źródło
0

Aby przechowywać obiekt, możesz utworzyć litery, za pomocą których można przenieść obiekt z łańcucha do obiektu (może to 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, a także jest bardzo eksperymentalna.

Seizefire
źródło
0

Znalazłem sposób, aby działał z obiektami, które mają cykliczne odwołania.

Stwórzmy obiekt z cyklicznymi referencjami.

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 tego zrobić JSON.stringifyz powodu okólników.

okrągły Wujek

LOCALSTORAGE.CYCLICJSONma .stringifyi .parsetak jak normalnie JSON, ale działa z obiektami z referencjami kołowymi. („Works” oznacza parsować (stringify (obj)) i obj są głęboko równe ORAZ mają identyczne zestawy „wewnętrznych równości”)

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

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

Wtedy recoveredbędzie „taki sam” dla 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 wdrożenie 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>

mathheadinclouds
źródło
-2

localStorage.setItem („użytkownik”, JSON.stringify (użytkownik));

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

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

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

localStorage.clear ();

manasa woddeyar manu
źródło
3
To jest 10-letnie pytanie. Czy uważasz, że twoja odpowiedź dodaje coś, czego nie obejmują jeszcze inne odpowiedzi?
Kristopher Johnson