如何在 HTML5 localStorage 中存储对象
问:
我想在 HTML5 localStorage 中存储一个 JavaScript 对象,但我的对象显然正在转换为字符串。
我可以使用 localStorage 存储和检索原始 JavaScript 类型和数组,但对象似乎不起作用。他们应该吗?
这是我的代码:
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 方法在存储之前将输入转换为字符串。
我在 Safari、Chrome 和 Firefox 中看到了这种行为,因此我认为这是我对 HTML5 Web Storage 规范的误解,而不是特定于浏览器的错误或限制。
我试图理解 2 Common infrastructure 中描述的 结构化克隆 算法。我不完全理解它在说什么,但也许我的问题与我的对象的属性不可枚举(???)有关。
有简单的解决方法吗?
更新:W3C 最终改变了对结构化克隆规范的看法,并决定更改规范以匹配实现。请参阅 12111 – spec for Storage object getItem(key) method does not match implementation behavior。所以这个问题不再是 100% 有效,但答案仍然可能是有趣的。
答1:
HuntsBot周刊–不定时分享成功产品案例,学习他们如何成功建立自己的副业–huntsbot.com
查看 Apple、Mozilla 和 Mozilla again 文档,该功能似乎仅限于处理字符串键/值对。
一种解决方法可以是在存储对象之前 stringify 您的对象,然后在您检索它时对其进行解析:
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 可以 setItem 抛出一些异常吗?
... 仅适用于具有循环引用的对象,JSON.stringify() 将引用的对象扩展为我们字符串化对象中的完整“内容”(隐式字符串化)。请参阅:stackoverflow.com/a/12659424/2044940
如果您必须处理大型数组或对象,这种方法的问题是性能问题。
@oligofren 是的,但正如maja 正确建议的那样 eval() => ,这是 eval() => 的好用处之一,您可以轻松检索函数代码 => 将其存储为字符串,然后 eval() 将其返回 :)
答2:
huntsbot.com提供全网独家一站式外包任务、远程工作、创意产品分享与订阅服务!
对 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);
}
由于 short-circuit evaluation,如果 key 不在存储中,getObject() 将立即返回 null。如果 value 是 “”(空字符串;JSON.parse() 无法处理),它也不会抛出 SyntaxError 异常。
我只想快速添加用法,因为我并没有立即清楚:var userObject = { userId: 24, name: 'Jack Bauer' }; 并设置它localStorage.setObject('user', userObject); 然后从存储中取回userObject = localStorage.getObject('user'); 如果需要,您甚至可以存储对象数组。
它只是布尔表达式。仅当左侧为真时才评估第二部分。在这种情况下,整个表达式的结果将来自右侧。它是基于布尔表达式评估方式的流行技术。
我在这里看不到局部变量和快捷方式评估的意义(除了性能改进之外)。如果 key 不在本地存储中,则 window.localStorage.getItem(key) 返回 null——它确实不抛出“非法访问”异常——并且 JSON.parse(null) 也返回 null——它确实not 也不会抛出异常,无论是在 Chromium 21 中还是根据 ES 5.1 section 15.12.2,因为 String(null) === "null" 可以解释为 JSON literal。
本地存储中的值始终是原始字符串值。所以这个快捷评估确实处理的是某人之前存储了 "" (空字符串)。因为它类型转换为 false 并且 JSON.parse("") 会引发 SyntaxError 异常,所以不会被调用。
这在 IE8 中不起作用,因此如果您需要支持它,最好使用已确认答案中的功能。
答3:
一个优秀的自由职业者,应该有对需求敏感和精准需求捕获的能力,而huntsbot.com提供了这个机会
您可能会发现使用这些方便的方法扩展 Storage 对象很有用:
Storage.prototype.setObject = function(key, value) {
this.setItem(key, JSON.stringify(value));
}
Storage.prototype.getObject = function(key) {
return JSON.parse(this.getItem(key));
}
通过这种方式,您可以获得您真正想要的功能,即使在 API 下仅支持字符串。
将 CMS 的方法封装成一个函数是一个好主意,它只需要一个功能测试:一个用于 JSON.stringify,一个用于 JSON.parse,另一个用于测试 localStorage 是否实际上可以设置和检索对象。修改宿主对象不是一个好主意;我宁愿将其视为单独的方法,而不是 localStorage.setObject。
如果存储的值为 "",则此 getObject() 将引发 SyntaxError 异常,因为 JSON.parse() 无法处理。有关详细信息,请参阅我对 Guria 答案的编辑。
只是我的两分钱,但我很确定像这样扩展供应商提供的对象不是一个好主意。
我完全同意@Sethen。请不要像这样对浏览器实现的全局变量进行猴子补丁。它可能会破坏代码,并且与将来可能在此全局中提供 setObject 方法的浏览器不兼容。
答4:
与HuntsBot一起,探索全球自由职业机会–huntsbot.com
为 Storage 对象创建外观是一个很棒的解决方案。这样,您可以实现自己的 get 和 set 方法。对于我的 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;
}
}
这几乎正是我所需要的。只需要在注释前加上 if (value == null) { return false } ,否则在检查 localStorage 上是否存在键时会导致错误。
这实际上很酷。同意@FrancescoFrapporti,您需要一个 if 来获取空值。我还添加了一个'|| value[0] == "[" ' 测试以防万一那里有一个数组。
好点,我会编辑这个。虽然你不需要 null 部分,但如果你需要,我推荐三个 ===。如果你使用 JSHint 或 JSLint,你会被警告不要使用 ==。
对于非忍者(比如我),有人可以提供这个答案的用法示例吗?是:data.set('username': 'ifedi', 'fullname': { firstname: 'Ifedi', lastname: 'Okonkwo'});?
如果要将键设置为 0、"" 或任何其他转换为 false 的值,则 set 函数将不起作用。相反,您应该写: if (!key || value === undefined) return; 这也可以让您为键存储一个“null”值。
答5:
huntsbot.com汇聚了国内外优秀的初创产品创意,可按收入、分类等筛选,希望这些产品与实践经验能给您带来灵感。
Stringify 并不能解决所有问题
似乎这里的答案并没有涵盖 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"
我不建议存储函数,因为 eval() 是邪恶的,可能会导致安全、优化和调试方面的问题。
通常,eval() 绝不应在 JavaScript 代码中使用。
私人会员
使用 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);
循环引用
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;
}
});
但是,找到存储循环引用的有效解决方案在很大程度上取决于需要解决的任务,并且恢复此类数据也并非易事。
Stack Overflow 上已经有一些问题处理这个问题:Stringify (convert to JSON) a JavaScript object with circular reference
因此,不用说 - 将数据存储到 Storage 应该基于简单数据副本的唯一前提。不是活的对象。
这些天可能会使用自定义 toJSON 而不是 toString() 。不幸的是,没有用于解析的对称等价物。
toJSON 不支持没有直接 json 表示的类型,如日期、正则表达式、函数和许多其他在我写完这个答案后添加到 JavaScript 中的新类型。
为什么在 localStorage.num (num = +localStorage.num) 前面加“+”?
@PeterMortensen 将存储的字符串转换回数字
答6:
huntsbot.com高效搞钱,一站式跟进超10+任务平台外包需求
有一个很棒的库,其中包含许多解决方案,因此它甚至支持称为 jStorage 的旧浏览器
你可以设置一个对象
$.jStorage.set(key, value)
并轻松检索
value = $.jStorage.get(key)
value = $.jStorage.get(key, "default value")
@SuperUberDuper jStorage 需要 Prototype、MooTools 或 jQuery
答7:
huntsbot.com提供全网独家一站式外包任务、远程工作、创意产品分享与订阅服务!
我在点击另一个已关闭的帖子后到达此帖子 - 标题为“如何在本地存储中存储数组?”。这很好,除了两个线程实际上都没有提供关于如何在 localStorage 中维护数组的完整答案 - 但是我已经设法根据两个线程中包含的信息制定了一个解决方案。
因此,如果其他人希望能够在数组中推送/弹出/移动项目,并且他们希望将该数组存储在 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 数组中存储简单字符串:
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
这是一组非常方便的方法,用于操作存储在 localStorage 或 sessionStorage 中的数组,值得称赞的地方远多于吸引人的地方。 @Andy Lorenz 感谢您抽出宝贵时间分享!
像这样对浏览器提供的全局补丁进行猴子补丁通常不是一个好主意。它可能会导致其他代码中断,并且它与未来可能希望在全局中发布自己的同名方法的浏览器不向前兼容。
@Flimm 我同意这样做通常不是一个好主意,但这种观点更多地基于理论而不是实践。例如,自从我在 2014 年发布以来,localStorage 或 sessionStorage 实现中的任何内容都没有发生任何已被破坏的更改。我怀疑他们永远不会诚实。但是,如果这种可能性对某人来说是一个问题——考虑风险是个人决定,而不是“你应该/不”——我的回答可以很容易地用作实现一个自定义数组类的蓝图,该类围绕实际的 localStorage /会话存储。
答8:
保持自己快人一步,享受全网独家提供的一站式外包任务、远程工作、创意产品订阅服务–huntsbot.com
理论上,可以使用函数存储对象:
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;
}
但是,函数序列化/反序列化是不可靠的,因为 it is implementation-dependent。
函数序列化/反序列化不可靠,因为 it is implementation-dependent。此外,您希望将 c.f[k] = escape(a[k]); 替换为 Unicode 安全的 c.f[k] = encodeURIComponent(a[k]);,并将 eval('b.' + k + ' = ' + unescape(data.f[k])); 替换为 b[k] = eval("(" + decodeURIComponent(data.f[k]) + ")");。括号是必需的,因为如果您的函数正确序列化,则可能是匿名的,这不是有效的 /Statement/(因此 eval())否则会抛出 SyntaxError 异常)。
而 typeof 是一个运算符,不要把它写成一个函数。将 typeof(a[k]) 替换为 typeof a[k]。
除了应用我的建议并强调该方法的不可靠性外,我还修复了以下错误: 1. 并非所有变量都已声明。 2. for-in 未针对自己的属性进行过滤。 3. 代码风格,包括引用,不一致。
@PointedEars 这有什么实际区别?规范说the use and placement of white space, line terminators, and semicolons within the representation String is implementation-dependent.我没有看到任何功能差异。
@Michael您引用的部分以Note *in particular* that …开头。但是返回值规范以 An implementation-dependent representation of the function is returned. This representation has the syntax of a FunctionDeclaration. 开头。返回值可以是 function foo () {}——假设是 conforming 实现。
答9:
huntsbot.com精选全球7大洲远程工作机会,涵盖各领域,帮助想要远程工作的数字游民们能更精准、更高效的找到对方。
建议对这里讨论的许多特性使用抽象库,以及更好的兼容性。有很多选择:
jStorage 或 simpleStorage ← 我的偏好
本地饲料
阿列克谢库利科夫/存储
草坪椅
Store.js ← 另一个不错的选择
我的天啊
本地数据存储
答10:
保持自己快人一步,享受全网独家提供的一站式外包任务、远程工作、创意产品订阅服务–huntsbot.com
您不能存储没有字符串格式的键值。
LocalStorage 仅支持键/值的字符串格式。
这就是为什么您应该将数据转换为字符串,无论它是数组还是对象。
要将数据存储在 localStorage 中,首先使用 JSON.stringify() 方法对其进行字符串化。
var myObj = [{name:"test", time:"Date 2017-02-03T08:38:04.449Z"}];
localStorage.setItem('item', JSON.stringify(myObj));
然后当你想检索数据时,你需要再次将字符串解析为对象。
var getObj = JSON.parse(localStorage.getItem('item'));
谢谢,我清除了本地存储的概念
答11:
huntsbot.com提供全网独家一站式外包任务、远程工作、创意产品分享与订阅服务!
您可以使用 localDataStorage 透明地存储 JavaScript 数据类型(Array、Boolean、Date、Float、Integer、String 和 Object)。它还提供轻量级的数据混淆,自动压缩字符串,促进键(名称)查询和(键)值查询,并通过为键加前缀来帮助在同一域内强制执行分段共享存储。
[免责声明] 我是该实用程序的作者 [/DISCLAIMER]
例子:
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
如您所见,原始值受到尊重。
这是一个很棒的资源,正是我所需要的。我正在使用 AngularJS 做 Ionic 应用程序,我需要在 localStorage 中保存某些 javascript 对象,到目前为止,我一直在做 JSON.parse 和 JSON.stringify,它们可以工作,但它比能够更麻烦只需使用像这样的实用程序。我要试试。
原文链接:https://www.huntsbot.com/qa/w73Q/how-to-store-objects-in-html5-localstorage?lang=zh_CN&from=csdn
huntsbot.com聚合了超过10+全球外包任务平台的外包需求,寻找外包任务与机会变的简单与高效。