javaScript:使用 JSON.parse 解析字符串化对象会删除引用
javaScript: Parsing a stringified object with JSON.parse removes references
我正在尝试深度克隆一个对象,例如 "a" 和 k = JSON.parse(JSON.stringify(a))。使用 stringify 方式很重要,因为我试图将对象保存到文件中,然后从中加载。
我偶然发现了克隆对象的引用问题,如下所示:
var obj={};
obj.importantProperty={s:2};
obj.c=obj.importantProperty;
obj.d=obj.importantProperty;
console.log( obj.c === obj.d ); // Returns true
var cloned = JSON.parse(JSON.stringify(obj));
console.log( cloned.c === cloned.d ); // Returns false
我需要在使用 JSON.parse 时保留引用,在上面的示例中它们不是。在我的项目中,对象要复杂得多,但最终归结为上面的例子。
提前感谢所有帮助我的人:)
使用 JSON 无法达到您想要的结果,因为 JSON 格式只能包含有限数量的数据类型 (http://json.org/),并且当您将对象字符串化为 JSON 一些信息丢失了。
可能还有其他类型的序列化技术,但我建议您寻找另一种存储数据的方法。
执行此类操作的正确方法是分别存储公共引用对象并通过 ID 引用它。
例如,您可以将 importantProperty
对象保存在数组中并使用索引作为 ID:
var importantProperties = [
{ s: 1 },
{ s: 2 },
{ s: 3 }
];
var obj = {};
obj.importantProperty = importantProperties[1];
obj.c = obj.importantProperty;
obj.d = obj.importantProperty;
然后当您将对象字符串化时,您将引用的对象替换为它的索引:
var stringified = JSON.stringify(obj, function(key, value) {
if (key) {
return importantProperties.indexOf(value);
}
return value;
});
console.log(stringified);
// prints {"importantProperty":1,"c":1,"d":1}
然后当您解析时,您只需反转过程即可恢复引用:
var parsed = JSON.parse(stringified, function(key, value) {
if (key) {
return importantProperties[value];
}
return value;
});
console.log(parsed.c === parsed.d && parsed.d === parsed.importantProperty);
// prints true
现在,假设 obj
中的所有属性都是 importantProperties
数组中的对象,上面的示例适用于您的示例代码。如果不是这种情况,并且只有某些属性是 importantProperties
对象,则您需要在 replacing/reviving.
时进行检查
假设只有 "importantProperty"、"c" 和 "d" 属性是这样的对象:
if (['importantProperty', 'c', 'd'].includes(key))
而不仅仅是 if (key)
如果这还不够好,并且您不希望 属性 名称与该值是否为 importantProperties
对象有任何关系,您需要与标识符一起在值中指明这一点。这是如何完成此操作的示例:
// Replacing
JSON.stringify(obj, function(k, value) {
if (importantProperties.includes(value)) {
return 'ImportantProperty['
+ importantProperties.indexOf(value)
+ ']';
}
return value;
});
// Reviving
JSON.parse(stringified, function(k, value) {
if (/^ImportantProperty\[\d+\]$/.test(value)) {
var index = Number( value.match(/\d+/)[0] );
return importantProperties[index];
}
return value;
});
我正在尝试深度克隆一个对象,例如 "a" 和 k = JSON.parse(JSON.stringify(a))。使用 stringify 方式很重要,因为我试图将对象保存到文件中,然后从中加载。
我偶然发现了克隆对象的引用问题,如下所示:
var obj={};
obj.importantProperty={s:2};
obj.c=obj.importantProperty;
obj.d=obj.importantProperty;
console.log( obj.c === obj.d ); // Returns true
var cloned = JSON.parse(JSON.stringify(obj));
console.log( cloned.c === cloned.d ); // Returns false
提前感谢所有帮助我的人:)
使用 JSON 无法达到您想要的结果,因为 JSON 格式只能包含有限数量的数据类型 (http://json.org/),并且当您将对象字符串化为 JSON 一些信息丢失了。
可能还有其他类型的序列化技术,但我建议您寻找另一种存储数据的方法。
执行此类操作的正确方法是分别存储公共引用对象并通过 ID 引用它。
例如,您可以将 importantProperty
对象保存在数组中并使用索引作为 ID:
var importantProperties = [
{ s: 1 },
{ s: 2 },
{ s: 3 }
];
var obj = {};
obj.importantProperty = importantProperties[1];
obj.c = obj.importantProperty;
obj.d = obj.importantProperty;
然后当您将对象字符串化时,您将引用的对象替换为它的索引:
var stringified = JSON.stringify(obj, function(key, value) {
if (key) {
return importantProperties.indexOf(value);
}
return value;
});
console.log(stringified);
// prints {"importantProperty":1,"c":1,"d":1}
然后当您解析时,您只需反转过程即可恢复引用:
var parsed = JSON.parse(stringified, function(key, value) {
if (key) {
return importantProperties[value];
}
return value;
});
console.log(parsed.c === parsed.d && parsed.d === parsed.importantProperty);
// prints true
现在,假设 obj
中的所有属性都是 importantProperties
数组中的对象,上面的示例适用于您的示例代码。如果不是这种情况,并且只有某些属性是 importantProperties
对象,则您需要在 replacing/reviving.
时进行检查
假设只有 "importantProperty"、"c" 和 "d" 属性是这样的对象:
if (['importantProperty', 'c', 'd'].includes(key))
而不仅仅是 if (key)
如果这还不够好,并且您不希望 属性 名称与该值是否为 importantProperties
对象有任何关系,您需要与标识符一起在值中指明这一点。这是如何完成此操作的示例:
// Replacing
JSON.stringify(obj, function(k, value) {
if (importantProperties.includes(value)) {
return 'ImportantProperty['
+ importantProperties.indexOf(value)
+ ']';
}
return value;
});
// Reviving
JSON.parse(stringified, function(k, value) {
if (/^ImportantProperty\[\d+\]$/.test(value)) {
var index = Number( value.match(/\d+/)[0] );
return importantProperties[index];
}
return value;
});