Javascript 在 HTML5 localStorage 中存储对象
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2010892/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Storing Objects in HTML5 localStorage
提问by Kristopher Johnson
I'd like to store a JavaScript object in HTML5 localStorage, but my object is apparently being converted to a string.
我想在 HTML5 中存储一个 JavaScript 对象localStorage,但我的对象显然正在转换为字符串。
I can store and retrieve primitive JavaScript types and arrays using localStorage, but objects don't seem to work. Should they?
我可以使用 存储和检索原始 JavaScript 类型和数组localStorage,但对象似乎不起作用。他们应该吗?
Here's my code:
这是我的代码:
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);
The console output is
控制台输出是
typeof testObject: object
testObject properties:
one: 1
two: 2
three: 3
typeof retrievedObject: string
Value of retrievedObject: [object Object]
It looks to me like the setItemmethod is converting the input to a string before storing it.
在我看来,该setItem方法在存储之前将输入转换为字符串。
I see this behavior in Safari, Chrome, and Firefox, so I assume it's my misunderstanding of the HTML5 Web Storagespec, not a browser-specific bug or limitation.
我在 Safari、Chrome 和 Firefox 中看到了这种行为,所以我认为这是我对HTML5 Web Storage规范的误解,而不是特定于浏览器的错误或限制。
I've tried to make sense of the structured clonealgorithm described in http://www.w3.org/TR/html5/infrastructure.html. I don't fully understand what it's saying, but maybe my problem has to do with my object's properties not being enumerable (???)
我试图理解http://www.w3.org/TR/html5/infrastructure.html 中描述的结构化克隆算法。我不完全明白它在说什么,但也许我的问题与我的对象的属性不可枚举有关 (???)
Is there an easy workaround?
有简单的解决方法吗?
Update: The W3C eventually changed their minds about the structured-clone specification, and decided to change the spec to match the implementations. See https://www.w3.org/Bugs/Public/show_bug.cgi?id=12111. So this question is no longer 100% valid, but the answers still may be of interest.
更新:W3C 最终改变了他们对结构化克隆规范的看法,并决定更改规范以匹配实现。参见https://www.w3.org/Bugs/Public/show_bug.cgi?id=12111。所以这个问题不再是 100% 有效,但答案仍然可能令人感兴趣。
回答by CMS
Looking at the Apple, Mozillaand Mozilla againdocumentation, the functionality seems to be limited to handle only string key/value pairs.
再次查看Apple、Mozilla和Mozilla文档,该功能似乎仅限于处理字符串键/值对。
A workaround can be to stringifyyour object before storing it, and later parse it when you retrieve it:
一种解决方法是在存储对象之前将其字符串化,然后在检索它时对其进行解析:
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));
回答by Guria
A minor improvement on a variant:
一个变体的小改进:
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);
}
Because of short-circuit evaluation, getObject()will immediatelyreturn nullif keyis not in Storage. It also will not throw a SyntaxErrorexception if valueis ""(the empty string; JSON.parse()cannot handle that).
由于短路评估,如果不在 StoragegetObject()中将立即返回。如果是(空字符串;无法处理),它也不会抛出异常。nullkeySyntaxErrorvalue""JSON.parse()
回答by Justin Voskuhl
You might find it useful to extend the Storage object with these handy methods:
您可能会发现使用这些方便的方法扩展 Storage 对象很有用:
Storage.prototype.setObject = function(key, value) {
this.setItem(key, JSON.stringify(value));
}
Storage.prototype.getObject = function(key) {
return JSON.parse(this.getItem(key));
}
This way you get the functionality that you really wanted even though underneath the API only supports strings.
这样,即使在 API 下仅支持字符串,您也可以获得真正想要的功能。
回答by Alex Grande
Extending the Storage object is an awesome solution. For my API, I have created a facade for localStorage and then check if it is an object or not while setting and getting.
扩展 Storage 对象是一个很棒的解决方案。对于我的 API,我为 localStorage 创建了一个外观,然后在设置和获取时检查它是否是一个对象。
var data = {
set: function(key, value) {
if (!key || !value) {return;}
if (typeof value === "object") {
value = JSON.stringify(value);
}
localStorage.setItem(key, value);
},
get: function(key) {
var value = localStorage.getItem(key);
if (!value) {return;}
// assume it is an object that has been stringified
if (value[0] === "{") {
value = JSON.parse(value);
}
return value;
}
}
回答by maja
Stringify doesn't solve all problems
Stringify 并不能解决所有问题
It seems that the answers here don't cover all types that are possible in JavaScript, so here are some short examples on how to deal with them correctly:
似乎这里的答案并没有涵盖 JavaScript 中所有可能的类型,所以这里有一些关于如何正确处理它们的简短示例:
//Objects and Arrays:
var obj = {key: "value"};
localStorage.object = JSON.stringify(obj); //Will ignore private members
obj = JSON.parse(localStorage.object);
//Boolean:
var bool = false;
localStorage.bool = bool;
bool = (localStorage.bool === "true");
//Numbers:
var num = 42;
localStorage.num = num;
num = +localStorage.num; //short for "num = parseFloat(localStorage.num);"
//Dates:
var date = Date.now();
localStorage.date = date;
date = new Date(parseInt(localStorage.date));
//Regular expressions:
var regex = /^No\.[\d]*$/i; //usage example: "No.42".match(regex);
localStorage.regex = regex;
var components = localStorage.regex.match("^/(.*)/([a-z]*)$");
regex = new RegExp(components[1], components[2]);
//Functions (not recommended):
function func(){}
localStorage.func = func;
eval( localStorage.func ); //recreates the function with the name "func"
I do not recommendto store functions because eval()is evil can lead to issues regarding security, optimisation and debugging.
In general, eval()should never be used in JavaScript code.
我不建议存储函数,因为eval()它可能会导致有关安全、优化和调试的问题。一般来说,eval()绝不应该在 JavaScript 代码中使用。
Private members
私人会员
The problem with using JSON.stringify()for storing objects is, that this function can not serialise private members.
This issue can be solved by overwriting the .toString()method (which is called implicitly when storing data in web storage):
JSON.stringify()用于存储对象的问题是,这个函数不能序列化私有成员。这个问题可以通过覆盖.toString()方法来解决(在网络存储中存储数据时会隐式调用):
//Object with private and public members:
function MyClass(privateContent, publicContent){
var privateMember = privateContent || "defaultPrivateValue";
this.publicMember = publicContent || "defaultPublicValue";
this.toString = function(){
return '{"private": "' + privateMember + '", "public": "' + this.publicMember + '"}';
};
}
MyClass.fromString = function(serialisedString){
var properties = JSON.parse(serialisedString || "{}");
return new MyClass( properties.private, properties.public );
};
//Storing:
var obj = new MyClass("invisible", "visible");
localStorage.object = obj;
//Loading:
obj = MyClass.fromString(localStorage.object);
Circular references
循环引用
Another problem stringifycan't deal with are circular references:
另一个stringify无法处理的问题是循环引用:
var obj = {};
obj["circular"] = obj;
localStorage.object = JSON.stringify(obj); //Fails
In this example, JSON.stringify()will throw a TypeError"Converting circular structure to JSON".
If storing circular references should be supported, the second parameter of JSON.stringify()might be used:
在这个例子中,JSON.stringify()将抛出一个TypeError"Converting circle structure to JSON"。如果应该支持存储循环引用,则JSON.stringify()可以使用第二个参数:
var obj = {id: 1, sub: {}};
obj.sub["circular"] = obj;
localStorage.object = JSON.stringify( obj, function( key, value) {
if( key == 'circular') {
return "$ref"+value.id+"$";
} else {
return value;
}
});
However, finding an efficient solution for storing circular references highly depends on the tasks that need to be solved, and restoring such data is not trivial either.
然而,找到一个有效的存储循环引用的解决方案很大程度上取决于需要解决的任务,恢复这些数据也不是一件容易的事。
There are already some question on SO dealing with this problem: Stringify (convert to JSON) a JavaScript object with circular reference
已经有一些关于 SO 处理这个问题的问题:Stringify (convert to JSON) a JavaScript object with circle reference
回答by JProgrammer
There is a great library that wraps many solutions so it even supports older browsers called jStorage
有一个很棒的库,它包含了许多解决方案,因此它甚至支持名为jStorage 的旧浏览器
You can set an object
你可以设置一个对象
$.jStorage.set(key, value)
And retrieve it easily
并轻松取回
value = $.jStorage.get(key)
value = $.jStorage.get(key, "default value")
回答by aster_x
In theory, it is possible to store objects with functions:
理论上,可以用函数来存储对象:
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;
}
However, Function serialization/deserialization is unreliable because it is implementation-dependent.
然而,函数序列化/反序列化是不可靠的,因为它是依赖于实现的。
回答by Andy Lorenz
I arrived at this post after hitting on another post that has been closed as a duplicate of this - titled 'how to store an array in localstorage?'. Which is fine except neither thread actually provides a full answer as to how you can maintain an array in localStorage - however I have managed to craft a solution based on information contained in both threads.
在找到另一篇已关闭的帖子后,我到达了这篇帖子,该帖子的标题是“如何在 localstorage 中存储数组?”。这很好,除了两个线程实际上都没有提供关于如何在 localStorage 中维护数组的完整答案 - 但是我已经设法根据两个线程中包含的信息制定了一个解决方案。
So if anyone else is wanting to be able to push/pop/shift items within an array, and they want that array stored in localStorage or indeed sessionStorage, here you go:
因此,如果其他人希望能够在数组中推送/弹出/移动项目,并且他们希望该数组存储在 localStorage 或实际上 sessionStorage 中,那么您就可以:
Storage.prototype.getArray = function(arrayName) {
var thisArray = [];
var fetchArrayObject = this.getItem(arrayName);
if (typeof fetchArrayObject !== 'undefined') {
if (fetchArrayObject !== null) { thisArray = JSON.parse(fetchArrayObject); }
}
return thisArray;
}
Storage.prototype.pushArrayItem = function(arrayName,arrayItem) {
var existingArray = this.getArray(arrayName);
existingArray.push(arrayItem);
this.setItem(arrayName,JSON.stringify(existingArray));
}
Storage.prototype.popArrayItem = function(arrayName) {
var arrayItem = {};
var existingArray = this.getArray(arrayName);
if (existingArray.length > 0) {
arrayItem = existingArray.pop();
this.setItem(arrayName,JSON.stringify(existingArray));
}
return arrayItem;
}
Storage.prototype.shiftArrayItem = function(arrayName) {
var arrayItem = {};
var existingArray = this.getArray(arrayName);
if (existingArray.length > 0) {
arrayItem = existingArray.shift();
this.setItem(arrayName,JSON.stringify(existingArray));
}
return arrayItem;
}
Storage.prototype.unshiftArrayItem = function(arrayName,arrayItem) {
var existingArray = this.getArray(arrayName);
existingArray.unshift(arrayItem);
this.setItem(arrayName,JSON.stringify(existingArray));
}
Storage.prototype.deleteArray = function(arrayName) {
this.removeItem(arrayName);
}
example usage - storing simple strings in localStorage array:
示例用法 - 在 localStorage 数组中存储简单字符串:
localStorage.pushArrayItem('myArray','item one');
localStorage.pushArrayItem('myArray','item two');
example usage - storing objects in sessionStorage array:
示例用法 - 在 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);
common methods to manipulate arrays:
操作数组的常用方法:
.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
回答by doublejosh
Recommend using an abstraction library for many of the features discussed here as well as better compatibility. Lots of options:
对于这里讨论的许多功能以及更好的兼容性,建议使用抽象库。很多选择:
- jStorageor simpleStorage<< my preference
- localForage
- alekseykulikov/storage
- Lawnchair
- Store.js<< another good option
- OMG
- jStorage或simpleStorage<< 我的偏好
- 本地草料
- 阿列克谢库利科夫/仓库
- 草坪椅
- Store.js<< 另一个不错的选择
- 我的天啊
回答by Mac
You can use localDataStorageto transparently store javascript data types (Array, Boolean, Date, Float, Integer, String and Object). It also provides lightweight data obfuscation, automatically compresses strings, facilitates query by key (name) as well as query by (key) value, and helps to enforce segmented shared storage within the same domain by prefixing keys.
您可以使用localDataStorage透明地存储 javascript 数据类型(数组、布尔值、日期、浮点数、整数、字符串和对象)。它还提供轻量级数据混淆,自动压缩字符串,促进按键(名称)查询以及按(键)值查询,并通过给键添加前缀帮助在同一域内实施分段共享存储。
[DISCLAIMER] I am the author of the utility [/DISCLAIMER]
[免责声明] 我是该实用程序的作者 [/免责声明]
Examples:
例子:
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 can see, the primitive values are respected.
如您所见,原始值受到尊重。

