Oh boy, you've snagged one of JavaScript's current, and most obvious flaws by effectively using this:
clubs[i].event[j].clubInfo = clubs[i];
You're creating an infinite reference - what do I mean by that? It's better displayed through an Array, if you'll oblige me:
let a=[]; a.push([a]);
This creates an infinite level array through self-reference, creating an incalculable depth. You see, though there's a 32(2^32-1) bit limit to an Array's length. This can be demonstrated easily:
Array(2**32); //Uncaught RangeError: Invalid array length
Presumably this was done to prevent browser memory from shorting but, strangely, there was never any consideration to the depth an array may contain. A minor side effect of this is that there is no depth
property, but a major side effect is that there is no protection from an infinite self-referencing array.
Getting Around It
The best way to get around this type of situation is to construct a new Object and assign properties from the old Object to the new. You can think of this as cloning. To do this you can utilize the assign
method:
Object.assign(constructor, **Array**/**Object**)
Example:
let a = []; a.push(Object.assign([], a));
Problem solved, right? uhhh... not quite Even though this can sometimes work, this still won't fix the issue of an Array or Object with more than shallow references. To get around that you have to use a combination of:
JSON.stringify(obj);
to break references
JSON.parse(JSON);
to remake your object, and
delete obj[deepReference];
deletion to stop any unforeseen issues with any superfluous data/references
None of this is ideal, but currently there is no way to completely separate all references inside of an object or array without recursive iteration.
To give you an example - In your case you're going to want to do something like this:
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);
Additional Info: Other watch outs
Similarly there are other issues in the language. A badly implemented Array constructor method. Array(n)
returns an Array with n
members. Why's that an issue? Everything in JavaScript that is declared and not instantiated is undefined
except the members of a freshly constructed array. They return empty. The issue with that is this means they have no mappable values. Ergo, all those sweet new functional ES Array methods are useless until the Array is filled. As an example:
Array(3).map((m, i) => i);
This results in well... the same thing you started with — when clearly it should provide a numbered array from 0-2. This is not as big of a deal as an infinite reference because you can work around it like this:
Array(3).fill(0).map((m,i) => i);
But it's effectively a wasted method call to take care of a problem that should be handled within construction.
Lastly, the fill
method — give it an object or an Array and it doesn't create n
individual object members. It creates n
references to a singular array or object and stuffs them into one array. At base logic, it sort of makes sense. As @FelixKling pointed out in the comments, it is fairly consistent with what you would expect, a.e. fill this array with this one thing. I still would debate a bit about it's functionality for two reasons.
In what situation would anyone need n
references stored in an Array to the same place in memory? Probably never. How often do people need an Array of similarly constructed Objects? All the time.
When passing an Object Literal for instance ( .fill({a:1})
)I can see the logic of filling the Array with references, even if it may not be wholly intuitive. If passed a Constructor- I would contest that it might make more sense to instantiate each member individually.
So there are many nuances, and inconsistencies with JavaScript that require knowledge to work around — and sadly, infinite reference, is one of them - but on the plus side the only way to typically realize these issues exist is to run into them headlong, so be thankful it wasn't in production!
Hope this helps! Happy Coding!