简体   繁体   中英

javascript delete object property is deleting entire object

I have a bizarre issue happening here. I am retrieving data from a remote source and I need to reformat it for better internal use

The data structure is like the following:

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: {...}
     ...
     ... 

I am looping through clubs and I want to assign the values of each club into its own events as property clubInfo . When I do that though, clubInfo becomes and endless chain of itself. So I delete the new clubInfo.events - and then everything but the root club[0] gets wiped out. I hope I am being clear, its hard to explain.

If I do this:

  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++) {
    }
  }

Then clubs becomes:

   clubs [
       0: events [
           clubInfo [
               0: events [
                   clubInfo [
                       0: events [
                          ....and goes on seemingly forever
                          ]
                       ]
                   ]
               ]   
            ]
        ]

If I do:

  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
    }
  }

Then all that remains of clubs is, in fact none of the other properties that have arrays events or others (several of them) are all gone.:

   clubs [
       0: { 
          propA: "blah",
          probB: "bar"
          }
       ]

Instead of delete, i have even tried just nulling clubInfo.events = null - but the same issue happens, everything gets wiped out.

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, ae fill this array with this one thing. I still would debate a bit about it's functionality for two reasons.

  1. 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 .

  2. 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!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM