我这里发生一个奇怪的问题。我正在从远程源检索数据,我需要对其重新格式化以更好地内部使用
数据结构如下:
clubs = [
0: {
propA: "blah",
probB: "bar",
events: [
0: {
data1: "foo",
data2: "bar"
}
1: {
data1: "this",
data2: "that"
}
1: {
propA: "hello",
probB: "bye",
events [
0: { ...}
1: {...}
...
...
我正在遍历俱乐部,我想将每个俱乐部的值分配给自己的events
作为属性clubInfo
。但是,当我这样做时,clubInfo变成了无止境的连锁店。因此,我删除了新的clubInfo.events
-然后除根球杆[0]之外的所有内容都被清除了。我希望我很清楚,很难解释。
如果我这样做:
for (var i=0; i<clubs.clubs.length;i++) {
var clubInfo = clubs.clubs[i] ;
var events = clubs.clubs[i].events ;
for (var j=0; j<events.length; j++) {
}
}
然后俱乐部变成:
clubs [
0: events [
clubInfo [
0: events [
clubInfo [
0: events [
....and goes on seemingly forever
]
]
]
]
]
]
如果我这样做:
for (var i=0; i<clubs.clubs.length;i++) {
var clubInfo = clubs.clubs[i] ;
delete clubInfo.events ;
var events = clubs.clubs[i].events ; // events is now empty
for (var j=0; j<events.length; j++) { // errors here because there is no length after the delete
}
}
那么俱乐部的所有剩余部分,实际上没有其他拥有数组events
或其他(其中的几个)的属性都消失了。:
clubs [
0: {
propA: "blah",
probB: "bar"
}
]
我什至没有尝试删除clubInfo.events = null
,而是删除了-但发生相同的问题,一切都消失了。
答案 0 :(得分:3)
哦,男孩,您已经有效地使用了此功能,从而抓住了JavaScript当前最明显的漏洞之一:
clubs[i].event[j].clubInfo = clubs[i];
您正在创建无限引用-这是什么意思?如果您愿意的话,最好通过Array进行显示:
let a=[]; a.push([a]);
这通过自引用创建了一个无限级数组,从而创建了无法计算的深度。您会看到,尽管数组的长度有32(2 ^ 32-1)位的限制。这很容易证明:
Array(2**32); //Uncaught RangeError: Invalid array length
大概是这样做是为了防止浏览器内存不足,但是奇怪的是,从未考虑过数组可能包含的深度。这样做的一个小的副作用是没有depth
属性,但是一个主要的副作用是没有针对无限的自引用数组的保护。
解决问题
解决这种情况的最佳方法是构造一个新的Object并将属性从旧的Object分配给新的Object。您可以将其视为克隆。为此,您可以使用assign
方法:
Object.assign(constructor, **Array**/**Object**)
示例:
let a = []; a.push(Object.assign([], a));
问题解决了吧? 呃...不完全,尽管有时可以奏效,但这仍然不能解决比浅引用还更多的数组或对象的问题。要解决这个问题,您必须使用以下组合:
JSON.stringify(obj);
中断引用
JSON.parse(JSON);
重新制作对象,然后
delete obj[deepReference];
删除,以停止任何多余的数据/引用中无法预料的问题
这都不是理想的选择,但是目前无法通过递归迭代将对象或数组中的所有引用完全分开。
举个例子-在您的情况下,您将想要执行以下操作:
for (var i = 0; i < clubs.length; i++) {
var clubInfo = clubs[i];
var events = clubs[i].events;
for (var j = 0; j < events.length; j++) {
let jsonTranslation = Object.assign({}, clubs[i]);
delete jsonTranslation.events;
jsonTranslation = JSON.stringify(jsonTranslation);
clubs[i].events[j].clubInfo = JSON.parse(jsonTranslation);
}
}
let clubs = [{
propA: "blah",
probB: "bar",
events: [{
data1: "foo",
data2: "bar"
},
{
data1: "this",
data2: "that"
}
]
}];
for (var i = 0; i < clubs.length; i++) {
var clubInfo = clubs[i];
var events = clubs[i].events;
for (var j = 0; j < events.length; j++) {
let jsonTranslation = Object.assign({}, clubs[i]);
delete jsonTranslation.events;
jsonTranslation = JSON.stringify(jsonTranslation);
clubs[i].events[j].clubInfo = JSON.parse(jsonTranslation);
}
}
console.log(clubs);
其他信息:其他注意事项
类似地,该语言还存在其他问题。一个不好实现的Array构造函数方法。 Array(n)
返回具有n
个成员的数组。为什么这是个问题? JavaScript中已声明且未实例化的所有内容均为undefined
,但新构造数组的成员除外。他们返回空。问题在于这意味着它们没有可映射的值。如此,所有这些不错的新功能性ES Array方法在数组被填充之前都是无用的。例如:
Array(3).map((m, i) => i);
这将导致...开始时的效果相同—显然,它应该提供0-2的数字数组。这与无限引用没什么大不了的,因为您可以像这样解决它:
Array(3).fill(0).map((m,i) => i);
但这实际上是浪费的方法调用,用于处理应在构造内处理的问题。
最后,fill
方法-给它一个对象或一个数组,它不会创建n
个单独的对象成员。它会创建对{strong>单个数组或对象的n
引用,并将它们填充到一个数组中。在基本逻辑上,这很有道理。正如@FelixKling在评论中指出的那样,它 与您的预期相当一致,即用这个 一个 一个东西填充这个数组。出于两个原因,我仍然会对其功能进行一些辩论。
在什么情况下,任何人都需要n
引用存储在数组中到内存中的相同位置? 可能永远不会。人们多久需要一次类似构造的对象阵列? 一直。
在传递对象文字时,例如(.fill({a:1})
,我可以看到用引用填充Array的逻辑,即使它可能不是很直观。如果通过了构造函数,我会争辩说分别实例化每个成员可能更有意义。
因此存在很多细微差别,并且与JavaScript的不一致要求知识变通-不幸的是,无限引用是其中之一-但从正面看,这是通常能够实现这些目的的唯一方法存在的问题是要一头扎进他们的,所以值得庆幸的是它不在生产中!
希望这会有所帮助!祝您编码愉快!