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 setItem
mé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.
fonte
Respostas:
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:
fonte
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/2044940Uma pequena melhoria em uma variante :
Por causa do curto-circuito avaliação ,
getObject()
irá imediatamente voltarnull
sekey
não está no armazenamento. Ele também não emitirá umaSyntaxError
exceção sevalue
for""
(a sequência vazia;JSON.parse()
não pode lidar com isso).fonte
var userObject = { userId: 24, name: 'Jack Bauer' };
e defini-lolocalStorage.setObject('user', userObject);
Depois recuperá-lo do armazenamentouserObject = localStorage.getObject('user');
Você pode até armazenar uma variedade de objetos, se quiser.key
não estiver no armazenamento local,window.localStorage.getItem(key)
retornaránull
- ele não lançará uma exceção de "acesso ilegal" - e tambémJSON.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 , poisString(null) === "null"
pode ser interpretado como um literal JSON .""
(a sequência vazia) antes. Porque ele converte em tipo parafalse
eJSON.parse("")
, o que geraria umaSyntaxError
exceção, não é chamado.Você pode achar útil estender o objeto Storage com estes métodos úteis:
Dessa forma, você obtém a funcionalidade que realmente queria, mesmo que por baixo da API seja compatível apenas com strings.
fonte
localStorage.setObject
.getObject()
gerará umaSyntaxError
exceção se o valor armazenado for""
, porqueJSON.parse()
não é possível lidar com isso. Veja minha edição da resposta de Guria para obter detalhes.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.
fonte
data.set('username': 'ifedi', 'fullname': { firstname: 'Ifedi', lastname: 'Okonkwo'});
:?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:
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):Referências circulares
Outro problema
stringify
não pode ser tratado são as referências circulares:Neste exemplo,
JSON.stringify()
lançará umaTypeError
"Convertendo estrutura circular para JSON" . Se o armazenamento de referências circulares deve ser suportado, o segundo parâmetro deJSON.stringify()
pode ser usado: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
fonte
Existe uma excelente biblioteca que agrupa muitas soluções e suporta até navegadores mais antigos chamados jStorage
Você pode definir um objeto
E recupere-o facilmente
fonte
Em teoria, é possível armazenar objetos com funções:
No entanto, a serialização / desserialização de funções não é confiável porque depende da implementação .
fonte
c.f[k] = escape(a[k]);
por Unicode-safec.f[k] = encodeURIComponent(a[k]);
eeval('b.' + k + ' = ' + unescape(data.f[k]));
withb[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 / (assimeval()
) lançaria umaSyntaxError
exceção caso contrário).typeof
é um operador , não o escreva como se fosse uma função. Substituatypeof(a[k])
portypeof a[k]
.for
-in
não foi filtrado por propriedades próprias. 3. O estilo do código, incluindo a referência, era inconsistente.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.Note *in particular* that …
. Mas a especificação do valor de retorno começa comAn implementation-dependent representation of the function is returned. This representation has the syntax of a FunctionDeclaration.
O valor de retorno pode serfunction foo () {}
- assumindo uma implementação em conformidade .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á:
exemplo de uso - armazenando cadeias simples na matriz localStorage:
exemplo de uso - armazenando objetos na matriz sessionStorage:
métodos comuns para manipular matrizes:
fonte
Recomenda o uso de uma biblioteca de abstração para muitos dos recursos discutidos aqui, além de melhor compatibilidade. Muitas opções:
fonte
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:
Como você pode ver, os valores primitivos são respeitados.
fonte
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).
(Isenção de responsabilidade: eu sou o autor.)
fonte
localStroage
; exp:var lsh = new localStorageHelper(); lsh.setItem('bob', 'bill');
também inclui eventos.Você pode usar o ejson para armazenar os objetos como cadeias.
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
fonte
Criei outro invólucro minimalista com apenas 20 linhas de código para permitir o uso como deveria:
https://github.com/zevero/simpleWebstorage
fonte
Para usuários do Typecript que desejam definir e obter propriedades digitadas:
Exemplo de uso :
fonte
https://github.com/adrianmay/rhaboo é uma camada de açúcar localStorage que permite escrever coisas como esta:
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.
fonte
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:
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 simplesget = localStorage.getItem;
não funcionafonte
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
localStorage
propriedade seja mágica:Se você precisar de vários:
Tudo o que você faz
prop
ou os objetos dentrostorage
serão salvos automaticamentelocalStorage
. Você está sempre brincando com um objeto real, para fazer coisas assim: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.fonte
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 () .
Então, quando você quiser recuperar dados, precisará analisar novamente a String para Objeto.
Espero que ajude.
fonte
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
Essa técnica causará algumas falhas se você usar a letra usada para dividir o objeto, e também é muito experimental.
fonte
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.
Não podemos fazer
JSON.stringify
aqui, por causa das referências circulares.LOCALSTORAGE.CYCLICJSON
tem.stringify
e.parse
como 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:
Então,
recovered
será "o mesmo" a obj, no seguinte sentido:Aqui está a implementação de
LOCALSTORAGE
fonte
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 ();
fonte