- Published on
如何在HTML5 localStorage和sessionStorage中存储对象
- Authors
- Name
在日常开发中,我们可能会遇到需要在HTML5的localStorage或sessionStorage中存储复杂对象的情况。有些对象包含循环引用,这使得我们无法使用普通的JSON.stringify
和JSON.parse
进行存储和恢复。本文将介绍如何实现这一需求,并提供一个涵盖循环引用处理的完整实现方案。
循环引用对象示例
首先,让我们创建一个包含循环引用的对象:
var 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;
在这种情况下,我们不能直接使用JSON.stringify
对其进行序列化,因为循环引用会导致错误。
解决方案:使用定制的localStorage
我们将在 LOCALSTORAGE.CYCLICJSON
中使用 stringify
和 parse
函数。这些函数专门处理循环引用对象。以下是实现代码:
var 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.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;
}
function stringify(obj) {
return JSON.stringify(decycleIntoForest(obj));
}
function parse(str) {
return deForestIntoCycle(JSON.parse(str));
}
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: {
decycleIntoForest: decycleIntoForest,
deForestIntoCycle: deForestIntoCycle,
stringify: stringify,
parse: parse
},
setObject: setObject,
getObject: getObject
};
})();
使用示例
我们可以测试上述方法来确保其正确功能:
LOCALSTORAGE.setObject('latinUncles', obj);
var recovered = LOCALSTORAGE.getObject('latinUncles');
// 验证恢复的对象和原对象是否等效
console.log([
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
]);
通过上述代码,我们可以存储和恢复包含循环引用的复杂对象,并确保恢复后的对象保持其原有的关系和数据。
总结
在这篇文章中,我们讨论了如何在HTML5的localStorage和sessionStorage中存储包含循环引用的复杂对象。通过使用自定义的 LOCALSTORAGE.CYCLICJSON
方法,我们避免了 JSON.stringify
和 JSON.parse
的局限性。希望这些内容对你在实际项目中有所帮助。
英文学术名称:how-to-store-objects-in-html5-localstorage-sessionstorage