将对象存储在HTML5 localStorage中

我想要在HTML5 localStorage存储JavaScript对象,但我的对象显然是被转换为string。

我可以使用localStorage存储和检索原始JavaScripttypes和数组,但对象似乎不工作。 他们应该吗?

这是我的代码:

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

控制台输出是

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

在我看来,像setItem方法在将input转换为string之前存储它。

我在Safari,Chrome和Firefox中看到了这种行为,所以我认为这是我对HTML5 Web存储规范的误解,而不是浏览器特定的错误或限制。

我试图理解http://www.w3.org/TR/html5/infrastructure.html中描述的结构化克隆algorithm。 我不完全明白它是什么意思,但也许我的问题与我的对象的属性不能被枚举(???)

有一个简单的解决方法?


更新:W3C最终改变了他们关于结构化克隆规范的想法,并决定改变规范来匹配实现。 见https://www.w3.org/Bugs/Public/show_bug.cgi?id=12111 。 所以这个问题不再100%有效,但答案仍然可能是有趣的。

看着苹果 , Mozilla和微软的文档,这个function似乎仅限于处理string键/值对。

解决方法是在存储对象之前对其进行string化 ,稍后在检索时parsing它。

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

贾斯汀的变体略有改善:

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

由于短路评估 ,如果key不在存储中, getObject()立即返回null 。 如果value"" (空string; JSON.parse()不能处理),它也不会抛出SyntaxErrorexception。

UPD。 添加了Mark Storer在评论中提到的variables

用这些方便的方法扩展存储对象可能会很有用:

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

这样你就可以获得你真正想要的function,即使在API下面只支持string。

扩展存储对象是一个很棒的解决scheme。 对于我的API,我已经为localStorage创build了一个门面,然后在设置和获取时检查它是否是一个对象。

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

有一个伟大的库包装了许多解决scheme,所以它甚至支持名为jStorage的旧浏览器

你可以设置一个对象

 $.jStorage.set(key, value) 

并轻松地检索它

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

Stringify并不能解决所有问题

看来这里的答案并不包括JavaScript中可能的所有types,所以这里有一些关于如何正确处理它们的简短例子:

 //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("^/(.*)/([az]*)$"); 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" 

我不build议存储函数,因为eval()是邪恶的可能导致有关安全性,优化和debugging的问题。 一般来说, eval()不应该在JavaScript代码中使用。

私人会员

使用JSON.stringify()存储对象的问题是,该函数不能序列化私有成员。 这个问题可以通过覆盖.toString()方法来解决.toString()当在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); 

循环引用

另一个问题stringify不能处理循环引用:

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

在这个例子中, JSON.stringify()会抛出一个TypeError “将循环结构转换为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; } }); 

然而,find一个有效的存储循环引用的解决scheme,很大程度上取决于需要解决的任务,恢复这些数据也不是微不足道的。

在处理这个问题上已经有一些问题了: 将具有循环引用的JavaScript对象进行Stringify(转换为JSON)

使用JSON对象进行本地存储:

//组

 var m={name:'Hero',Title:'developer'}; localStorage.setItem('us', JSON.stringify(m)); 

//得到

 var gm =JSON.parse(localStorage.getItem('us')); console.log(gm.name); 

//迭代所有本地存储键和值

 for (var i = 0, len = localStorage.length; i < len; ++i) { console.log(localStorage.getItem(localStorage.key(i))); } 

//删除

 localStorage.removeItem('us'); delete window.localStorage["us"]; 

从理论上讲,可以存储具有函数的对象:

 function store (a) { var c = {f: {}, d: {}}; for (var k in a) { if (a.hasOwnProperty(k) && typeof a[k] === 'function') { cf[k] = encodeURIComponent(a[k]); } } cd = 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; } 

但是,函数序列化/反序列化是不可靠的,因为它依赖于实现 。

您也可以重写默认的存储setItem(key,value)getItem(key)方法来处理像任何其他数据types的对象/数组。 这样,您可以像往常一样简单地调用localStorage.setItem(key,value)localStorage.getItem(key)

我还没有广泛的testing过,但是我似乎没有遇到过一个小问题,我一直在修补。

 Storage.prototype._setItem = Storage.prototype.setItem; Storage.prototype.setItem = function(key, value) { this._setItem(key, JSON.stringify(value)); } Storage.prototype._getItem = Storage.prototype.getItem; Storage.prototype.getItem = function(key) { try { return JSON.parse(this._getItem(key)); } catch(e) { return this._getItem(key); } } 

我碰到另一篇已被封闭的文章,名为“如何在本地存储中存储数组?”后到达这个post。 这是好的,除非线程实际上提供了一个完整的答案,你可以如何在localStorage中维护一个数组 – 但是我已经设法根据两个线程中包含的信息来创build一个解决scheme。

所以,如果其他人想要能够在数组中推送/popup/移动项目,并且他们希望该数组存储在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); } 

示例用法 – 在localStorage数组中存储简单的string:

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

示例用法 – 将对象存储在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); 

操作数组的常用方法:

 .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 

推荐使用抽象库来获得这里讨论的许多function以及更好的兼容性。 许多select:

  • jStorage或simpleStorage <<我的偏好
  • localForage
  • alekseykulikov /存储
  • Lawnchair
  • Store.js “另一个不错的select
  • 我的天啊

我已经修改了一个顶尖的答案一点。 如果不需要的话,我喜欢单function,而不是2。

 Storage.prototype.object = function(key, val) { if ( typeof val === "undefined" ) { var value = this.getItem(key); return value ? JSON.parse(value) : null; } else { this.setItem(key, JSON.stringify(val)); } } localStorage.object("test", {a : 1}); //set value localStorage.object("test"); //get value 

另外,如果没有设置值,则返回null而不是falsefalse有一些意义, null不。

提高@Guria的答案:

 Storage.prototype.setObject = function (key, value) { this.setItem(key, JSON.stringify(value)); }; Storage.prototype.getObject = function (key) { var value = this.getItem(key); try { return JSON.parse(value); } catch(err) { console.log("JSON parse failed for lookup of ", key, "\n error was: ", err); return null; } }; 

另一种select是使用现有的插件。

例如, persisto是一个开源项目,它为localStorage / sessionStorage提供了一个简单的接口,并自动化了表单字段(input,单选button和checkbox)的持久性。

persisto功能

(免责声明:我是作者。)

您可以使用ejson将对象存储为string。

EJSON是JSON的扩展,支持更多的types。 它支持所有JSON安全types,以及:

  • date(JavaScript Date
  • 二进制(JavaScript Uint8ArrayUint8Array的结果)
  • 用户定义的types(请参阅EJSON.addType 。例如, Mongo.ObjectID以此方式实现。)

所有的EJSON序列化也是有效的JSON。 例如,具有date和二进制缓冲区的对象将在EJSON中序列化为:

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

这是我使用ejson的localStorage包装器

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

我添加了一些types到我的包装,包括正则expression式和函数

http://rhaboo.org是一个localStorage糖层,可以让你写这样的东西:;

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

它不使用JSON.stringify / parse,因为这对大对象来说是不准确和缓慢的。 相反,每个terminal值都有自己的localStorage条目。

你可能猜到我可能与rhaboo有关;-)

阿德里安。

我做了另一个只有20行代码的简约包装,以便像它应该使用它:

 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

您可以使用localDataStorage透明地存储JavaScript数据types(数组,布尔值,date,浮点数,整数,string和对象)。 它还提供轻量级的数据模糊处理,自动压缩string,便于按键(名称)查询以及按(键)值查询,并有助于通过为键encryption在同一个域内实施分段共享存储。

[免责声明]我是实用程序的作者[/免责声明]

例子:

 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 

正如你所看到的,原始值是尊重的。

这里是@danott发布的代码的一些扩展版本

它也将执行从localstorage 删除值,并显示如何添加一个Getter和Setter图层,而不是

localstorage.setItem(preview, true)

你可以写

config.preview = true

好的,这里去了:

 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 

那么你可以用.bind(...)别名部分。 不过,我只是把它放进去,因为知道这件事真的很好。 我花了我几个小时来找出为什么一个简单的get = localStorage.getItem; 不工作

我认为要避免在本地,会话,cookies可以使用opendb库的那种问题..

前 – 你可以用这个片段解决这个问题

 // for set object in db db.local.setJSON("key", {name: "xyz"}); // for get object form db db.local.getJSON("key"); 

https://github.com/pankajbisht/openDB

我做了一件事情,不破坏现有的存储对象,但创build一个包装,所以你可以做你想做的。 结果是一个普通的对象,没有方法,像任何对象一样访问。

我做的东西。

如果你想要1个localStorage属性是神奇的:

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

如果你需要几个:

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

你所做的一切,或storage 的对象将被自动保存到localStorage 。 你总是在玩一个真实的物体,所以你可以做这样的事情:

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

跟踪对象的每个新对象都将被自动跟踪。

非常大的缺点:它取决于Object.observe()所以它的浏览器支持非常有限。 而且看起来它不会很快成为Firefox或者Edge。

看看这个

比方说,你有以下数组叫做电影:

 var movies = ["Reservoir Dogs", "Pulp Fiction", "Jackie Brown", "Kill Bill", "Death Proof", "Inglourious Basterds"]; 

使用stringify函数,可以使用以下语法将电影数组转换为string:

 localStorage.setItem("quentinTarantino", JSON.stringify(movies)); 

请注意,我的数据存储在名为quentinTarantino的密钥下。

检索您的数据

 var retrievedData = localStorage.getItem("quentinTarantino"); 

要从string转换回对象,请使用JSONparsing函数:

 var movies2 = JSON.parse(retrievedData); 

您可以调用您的电影2上的所有数组方法

要存储一个对象,你可以使用一个字母来获得一个string中的对象到一个对象(可能没有意义)。 例如

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

如果您使用您用来分割对象的字母,这种技巧会造成一些小问题,而且这也是非常实验性的。

一个使用localStorage来跟踪从联系人收到的消息的库的一个小例子:

 // This class is supposed to be used to keep a track of received message per contacts. // You have only four methods: // 1 - Tells you if you can use this library or not... function isLocalStorageSupported(){ if(typeof(Storage) !== "undefined" && window['localStorage'] != null ) { return true; } else { return false; } } // 2 - Give the list of contacts, a contact is created when you store the first message function getContacts(){ var result = new Array(); for ( var i = 0, len = localStorage.length; i < len; ++i ) { result.push(localStorage.key(i)); } return result; } // 3 - store a message for a contact function storeMessage(contact, message){ var allMessages; var currentMessages = localStorage.getItem(contact); if(currentMessages == null){ var newList = new Array(); newList.push(message); currentMessages = JSON.stringify(newList); } else { var currentList =JSON.parse(currentMessages); currentList.push(message); currentMessages = JSON.stringify(currentList); } localStorage.setItem(contact, currentMessages); } // 4 - read the messages of a contact function readMessages(contact){ var result = new Array(); var currentMessages = localStorage.getItem(contact); if(currentMessages != null){ result =JSON.parse(currentMessages); } return result; } 

循环遍历本地存储

 var retrievedData = localStorage.getItem("MyCart"); retrievedData.forEach(function (item) { console.log(item.itemid); })