Armazenando objetos em HTML5 localStorage


2511

Gostaria de armazenar um objeto JavaScript em HTML5 localStorage, mas aparentemente meu objeto está sendo convertido em uma string.

Posso armazenar e recuperar tipos e matrizes JavaScript primitivos usando localStorage, mas os objetos não parecem funcionar. Eles deveriam?

Aqui está o meu código:

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

A saída do console é

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

Parece-me que o setItemmétodo está convertendo a entrada em uma string antes de armazená-la.

Vejo esse comportamento no Safari, Chrome e Firefox, por isso suponho que seja meu mal-entendido sobre as especificações do HTML5 Web Storage , não um bug ou limitação específica do navegador.

Tentei entender o algoritmo de clone estruturado descrito em http://www.w3.org/TR/html5/infrastructure.html . Não entendo completamente o que está dizendo, mas talvez o meu problema esteja relacionado às propriedades do meu objeto não serem enumeráveis ​​(???)

Existe uma solução fácil?


Atualização: O W3C finalmente mudou de idéia sobre a especificação de clone estruturado e decidiu alterar a especificação para corresponder às implementações. Consulte https://www.w3.org/Bugs/Public/show_bug.cgi?id=12111 . Portanto, essa pergunta não é mais 100% válida, mas as respostas ainda podem ser interessantes.


17
BTW, sua leitura do "algoritmo de clone estruturado" está correta, apenas que a especificação foi alterada de valores somente de cadeia para isso após o término das implementações. Arquivei o bug bugzilla.mozilla.org/show_bug.cgi?id=538142 no mozilla para rastrear esse problema.
Nickolay

2
Isto parece como um trabalho para indexedDB ...
markasoftware

1
Que tal armazenar uma matriz de objetos no localStorage? Estou enfrentando o mesmo problema que está sendo convertido em string.
Jayant Pareek

1
você poderia apenas serializar a matriz? como armazenar com JSON stringify e analisar novamente ao carregar?
Brandito 12/0318

1
Você pode usar localDataStorage para armazenar de forma transparente tipos de dados javascript (Array, Boolean, Date, Float, Integer, String e Object)
Mac

Respostas:


3172

Olhando para a documentação da Apple , Mozilla e Mozilla novamente , a funcionalidade parece ser limitada para lidar apenas com pares chave / valor de cadeia.

Uma solução alternativa pode ser restringir o objeto antes de armazená-lo e analisá-lo posteriormente quando você o recuperar:

var testObject = { 'one': 1, 'two': 2, 'three': 3 };

// Put the object into storage
localStorage.setItem('testObject', JSON.stringify(testObject));

// Retrieve the object from storage
var retrievedObject = localStorage.getItem('testObject');

console.log('retrievedObject: ', JSON.parse(retrievedObject));

160
observe que todos os metadados serão removidos. você apenas obtém um objeto com os pares de valores-chave, portanto, qualquer objeto com comportamento precisa ser reconstruído.
Oligofren

5
O @CMS pode setItem lançar alguma exceção se os dados estiverem acima da capacidade?
Ashish Negi 26/03

3
... aplica-se apenas a objetos com referências circulares, JSON.stringify()expande o objeto referenciado para todo o seu "conteúdo" (implicitamente especificado) no objeto que nós submetemos. Veja: stackoverflow.com/a/12659424/2044940
CodeManX

3
O problema dessa abordagem são problemas de desempenho, se você precisar lidar com matrizes ou objetos grandes.
Mark

3
@oligofren true, mas como maja sugeriu corretamente eval () =>, este é um dos bons usos, você pode recuperar facilmente o código de função => armazená-lo como string e depois eval () de volta :)
jave.web

621

Uma pequena melhoria em uma variante :

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

Por causa do curto-circuito avaliação , getObject()irá imediatamente voltar nullse keynão está no armazenamento. Ele também não emitirá uma SyntaxErrorexceção se valuefor ""(a sequência vazia; JSON.parse()não pode lidar com isso).


48
Eu só quero adicionar rapidamente o uso, pois não ficou imediatamente claro para mim: var userObject = { userId: 24, name: 'Jack Bauer' }; e defini-lo localStorage.setObject('user', userObject); Depois recuperá-lo do armazenamento userObject = localStorage.getObject('user'); Você pode até armazenar uma variedade de objetos, se quiser.
Zuallauz

8
É apenas expressão booleana. A segunda parte é avaliada apenas se a esquerda for verdadeira. Nesse caso, o resultado de toda a expressão será da parte correta. É uma técnica popular baseada na maneira como as expressões booleanas são avaliadas.
Guria

4
Não vejo o ponto da variável local e a avaliação do atalho aqui (pequenas melhorias de desempenho à parte). Se keynão estiver no armazenamento local, window.localStorage.getItem(key)retornará null- ele não lançará uma exceção de "acesso ilegal" - e também JSON.parse(null)retornará null- também não lançará uma exceção, nem no Chromium 21 nem no seção 15.12.2 do ES 5.1 , pois String(null) === "null"pode ser interpretado como um literal JSON .
PointedEars

6
Os valores no armazenamento local são sempre valores primitivos de string. Então, o que essa avaliação de atalho trata é quando alguém armazenou ""(a sequência vazia) antes. Porque ele converte em tipo para falsee JSON.parse(""), o que geraria uma SyntaxErrorexceção, não é chamado.
PointedEars

2
Isso não funcionará no IE8, por isso é melhor usar as funções na resposta confirmada, se precisar apoiá-lo.
Ezeke

220

Você pode achar útil estender o objeto Storage com estes métodos úteis:

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

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

Dessa forma, você obtém a funcionalidade que realmente queria, mesmo que por baixo da API seja compatível apenas com strings.


13
Agrupar a abordagem do CMS em uma função é uma boa idéia, ela só precisa de testes de recursos: um para JSON.stringify, um para JSON.parse e outro para testar se o localStorage pode de fato definir e recuperar um objeto. Modificar objetos do host não é uma boa ideia; Eu preferiria ver isso como um método separado e não como localStorage.setObject.
Garrett

4
Isso getObject()gerará uma SyntaxErrorexceção se o valor armazenado for "", porque JSON.parse()não é possível lidar com isso. Veja minha edição da resposta de Guria para obter detalhes.
PointedEars

9
Apenas meus dois centavos, mas tenho certeza de que não é uma boa idéia estender objetos fornecidos pelo fornecedor como este.
Sethen 6/07/07


73

Estender o objeto Storage é uma solução incrível. Para minha API, criei uma fachada para localStorage e verifique se é um objeto ou não durante a configuração e a obtenção.

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
Isso foi quase exatamente o que eu precisava. Só tinha que adicionar if (value == null) {return false} antes do comentário, caso contrário, isso resultaria em erro ao verificar a existência de uma chave no localStorage.
Francesco Frapporti 8/03/12

2
Isso é bem legal, na verdade. Concordo com @FrancescoFrapporti, você precisa de um if lá para valores nulos. Eu também adicionei um '|| valor [0] == "[" 'teste caso haja uma matriz lá.
rob_james

Bom ponto, eu vou editar isso. Embora você não precise da parte nula, mas se precisar, recomendo três ===. Se você usar JSHint ou JSLint, será avisado contra o uso de ==.
Alex Grande

3
E para não-ninjas (como eu), alguém poderia fornecer um exemplo de uso para esta resposta? É data.set('username': 'ifedi', 'fullname': { firstname: 'Ifedi', lastname: 'Okonkwo'});:?
Ifedi Okonkwo

Sim, de fato! Quando superei meu desejo de ser alimentado com colher, peguei o código para testar e o peguei. Eu acho que essa resposta é ótima porque 1) Ao contrário da resposta aceita, leva tempo para fazer certas verificações nos dados da string e 2) Ao contrário da próxima, ela não estende um objeto nativo.
Ifedi Okonkwo

64

Stringify não resolve todos os problemas

Parece que as respostas aqui não cobrem todos os tipos possíveis em JavaScript, então, aqui estão alguns exemplos curtos de como lidar com elas corretamente:

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

Eu não recomendo armazenar funções, porque o eval()mal pode levar a problemas de segurança, otimização e depuração. Em geral,eval() nunca deve ser usado no código JavaScript.

Membros privados

O problema de usar JSON.stringify()para armazenar objetos é que essa função não pode serializar membros particulares. Esse problema pode ser resolvido sobrescrevendo o.toString() método (chamado implicitamente ao armazenar dados no armazenamento na web):

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

Referências circulares

Outro problema stringifynão pode ser tratado são as referências circulares:

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

Neste exemplo, JSON.stringify()lançará uma TypeError "Convertendo estrutura circular para JSON" . Se o armazenamento de referências circulares deve ser suportado, o segundo parâmetro de JSON.stringify()pode ser usado:

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

No entanto, encontrar uma solução eficiente para armazenar referências circulares depende muito das tarefas que precisam ser resolvidas, e a restauração desses dados também não é trivial.

Já existe alguma pergunta sobre o SO que lida com esse problema: Stringify (converter para JSON) um objeto JavaScript com referência circular


2
Portanto, e nem é preciso dizer - o armazenamento de dados no Storage deve basear-se na única premissa de cópias de dados simples. Objetos não ativos.
Roko C. Buljan 24/03

51

Existe uma excelente biblioteca que agrupa muitas soluções e suporta até navegadores mais antigos chamados jStorage

Você pode definir um objeto

$.jStorage.set(key, value)

E recupere-o facilmente

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

2
@SuperUberDuper jStorage requer Prototype, MooTools ou jQuery
JProgrammer

28

Em teoria, é possível armazenar objetos com funções:

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

No entanto, a serialização / desserialização de funções não é confiável porque depende da implementação .


1
A serialização / desserialização da função não é confiável porque depende da implementação . Além disso, você deseja substituir c.f[k] = escape(a[k]); por Unicode-safe c.f[k] = encodeURIComponent(a[k]);e eval('b.' + k + ' = ' + unescape(data.f[k]));with b[k] = eval("(" + decodeURIComponent(data.f[k]) + ")");. Os parênteses são necessários porque sua função, se serializada corretamente, provavelmente será anônima, o que não é o que é válido / Statement / (assim eval()) lançaria uma SyntaxErrorexceção caso contrário).
PointedEars

E typeofé um operador , não o escreva como se fosse uma função. Substitua typeof(a[k])por typeof a[k].
PointedEars

Além de aplicar minhas sugestões e enfatizar a falta de confiabilidade da abordagem, corrigi os seguintes erros: 1. Nem todas as variáveis ​​foram declaradas. 2. for- innão foi filtrado por propriedades próprias. 3. O estilo do código, incluindo a referência, era inconsistente.
PointedEars

@PointedEars, que diferença prática isso faz? a especificação diz the use and placement of white space, line terminators, and semicolons within the representation String is implementation-dependent. que não vejo diferenças funcionais.
Michael Michael

@ Michael A parte que você citou começa com Note *in particular* that … . Mas a especificação do valor de retorno começa com An implementation-dependent representation of the function is returned. This representation has the syntax of a FunctionDeclaration.O valor de retorno pode ser function foo () {}- assumindo uma implementação em conformidade .
PointedEars

22

Cheguei a este post depois de bater em outro post que foi fechado como uma duplicata - intitulado 'como armazenar uma matriz no armazenamento local?'. O que é bom, exceto que nenhum thread realmente fornece uma resposta completa sobre como você pode manter uma matriz no localStorage - no entanto, eu consegui criar uma solução com base nas informações contidas nos dois threads.

Portanto, se alguém quiser empurrar / pop / shift itens dentro de uma matriz e deseja que essa matriz seja armazenada em localStorage ou mesmo sessionStorage, aqui está:

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

exemplo de uso - armazenando cadeias simples na matriz localStorage:

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

exemplo de uso - armazenando objetos na matriz 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);

métodos comuns para manipular matrizes:

.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

Esse é um conjunto muito útil de métodos para manipular matrizes armazenadas em localStorage ou sessionStorage e merece muito mais crédito do que é atraído. @ Andy Lorenz Obrigado por compartilhar o seu tempo!
Velojet



6

Você pode usar localDataStorage para armazenar de forma transparente tipos de dados javascript (matriz, booleano, data, flutuante, número inteiro, sequência e objeto). Ele também fornece ofuscação leve de dados, compacta automaticamente as seqüências de caracteres, facilita a consulta por chave (nome) e consulta por valor (chave) e ajuda a impor o armazenamento compartilhado segmentado no mesmo domínio, prefixando as chaves.

[AVISO LEGAL] Eu sou o autor do utilitário [/ AVISO LEGAL]

Exemplos:

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

Como você pode ver, os valores primitivos são respeitados.


1
Este é um recurso brilhante e exatamente o que eu preciso. Estou fazendo aplicativos Ionic com AngularJS, onde preciso salvar certos objetos javascript em localStorage e, até este momento, acabei de fazer JSON.parse e JSON.stringify, e eles funcionam, mas é um pouco mais complicado do que poder para usar apenas um utilitário como este. Eu vou tentar.
Nmuta 21/01

4

Outra opção seria usar um plugin existente.

Por exemplo, persisto é um projeto de código aberto que fornece uma interface fácil para localStorage / sessionStorage e automatiza a persistência para campos de formulário (entrada, botões de opção e caixas de seleção).

recursos persisto

(Isenção de responsabilidade: eu sou o autor.)


Ainda trabalhando no meu leia-me, mas minha versão não requer jQuery, como parece persisto, mas fornece uma alternativa para lidar com objetos do elemento jQuery. Adicionarei mais no futuro próximo, à medida que for trabalhando mais, para ajudá-lo a lidar ainda mais com diferentes objetos jQuery e manter coisas como dados persistentes. Além disso, +1 por tentar fornecer uma solução mais simples! Além disso, ele usa todos os métodos tradicionais de localStroage; exp: var lsh = new localStorageHelper(); lsh.setItem('bob', 'bill'); também inclui eventos.
SpYk3HH 11/07/19

4

Você pode usar o ejson para armazenar os objetos como cadeias.

EJSON é uma extensão do JSON para suportar mais tipos. Ele suporta todos os tipos seguros para JSON, bem como:

Todas as serializações de EJSON também são JSON válidas. Por exemplo, um objeto com uma data e um buffer binário seria serializado no EJSON como:

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

Aqui está o meu wrapper localStorage usando o ejson

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

Adicionei alguns tipos ao meu wrapper, incluindo funções e expressões regulares


2

Criei outro invólucro minimalista com apenas 20 linhas de código para permitir o uso como deveria:

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

Para usuários do Typecript que desejam definir e obter propriedades digitadas:

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

Exemplo de uso :

// 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 é uma camada de açúcar localStorage que permite escrever coisas como esta:

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

Ele não usa JSON.stringify / parse porque isso seria impreciso e lento em objetos grandes. Em vez disso, cada valor do terminal possui sua própria entrada localStorage.

Você provavelmente pode adivinhar que eu posso ter algo a ver com rhaboo.


1

Aqui está uma versão extendida do código postado por @danott

Ele também vai implementar exclusão valor de localStorage e mostra como adiciona uma camada Getter e Setter então ao invés de

localstorage.setItem(preview, true)

você pode escrever

config.preview = true

Ok, aqui estavam:

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

Bem, você pode remover os aliases com .bind(...). No entanto, eu apenas o coloquei, já que é muito bom saber sobre isso. Levei horas para descobrir por que um simples get = localStorage.getItem;não funciona


1

Fiz uma coisa que não quebra os objetos de armazenamento existentes, mas cria um invólucro para que você possa fazer o que quiser. O resultado é um objeto normal, sem métodos, com acesso como qualquer objeto.

A coisa que eu fiz.

Se você deseja que uma localStoragepropriedade seja mágica:

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

Se você precisar de vários:

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

Tudo o que você faz propou os objetos dentro storage serão salvos automaticamente localStorage. Você está sempre brincando com um objeto real, para fazer coisas assim:

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

E todo novo objeto dentro de um objeto rastreado será rastreado automaticamente.

A grande desvantagem: depende, Object.observe()portanto, tem um suporte de navegador muito limitado. E não parece que chegará ao Firefox ou Edge tão cedo.


1

Você não pode armazenar o valor da chave sem o formato String .

LocalStorage suporta apenas o formato String para chave / valor.

É por isso que você deve converter seus dados em string, independentemente do que seja Array ou Object .

Para armazenar dados no localStorage, primeiro, especifique-os usando o método JSON.stringify () .

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

Então, quando você quiser recuperar dados, precisará analisar novamente a String para Objeto.

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

Espero que ajude.


0

Para armazenar um objeto, você pode criar letras que podem ser usadas para obter um objeto de uma string para um objeto (pode não fazer sentido). Por exemplo

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

Essa técnica causará algumas falhas se você usar a letra usada para dividir o objeto, e também é muito experimental.


0

Eu encontrei uma maneira de fazê-lo funcionar com objetos que possuem referências cíclicas.

Vamos criar um objeto com referências cíclicas.

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;

Não podemos fazer JSON.stringifyaqui, por causa das referências circulares.

circularUncle

LOCALSTORAGE.CYCLICJSONtem .stringifye .parsecomo normalJSON , mas funciona com objetos com referências circulares. ("Obras" significa análise (stringify (obj)) e obj são profundamente iguais E têm conjuntos idênticos de 'igualdades internas')

Mas podemos apenas usar os atalhos:

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

Então, recoveredserá "o mesmo" a obj, no seguinte sentido:

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

Aqui está a implementação de 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 ('usuário', JSON.stringify (usuário));

Em seguida, para recuperá-lo da loja e convertê-lo em um objeto novamente:

var usuário = JSON.parse (localStorage.getItem ('usuário'));

Se precisarmos excluir todas as entradas da loja, podemos simplesmente fazer:

localStorage.clear ();


3
Esta é uma pergunta de 10 anos de idade. Você acha que sua resposta adiciona algo ainda não coberto pelas outras respostas?
Kristopher Johnson
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.