0

First let me apologize for my poor english and javascript skills, I am a beginner.

Here is my problem: I would like to copy the key and values of a source object (words.wordFailed[ tmp ]) into a target object (words.wordsFailedOnly[ counter ]):

words.wordsFailedOnly[ counter ] = words.wordsFailed[ tmp ];

Once this is done I add a new key/value pair to the target:

words.wordsFailedOnly[ counter ][ 'newKey' ]  = tmp;

Even though I don't assign the new key to the source object, after the first foreach loop has been executed, I noticed that the source object has also this new key "newKey".

Here is the complete code portion:

                settings.languages.forEach( function( lang ) { 

                    let counter = 0;

                    Object.keys( words.wordsFailed ).forEach( function ( tmp ) {

                        if ( words.wordsFailed[ tmp ][ lang ] > 0 ) {

                            counter++;
                            
                            words.wordsFailedOnly[ counter ] = words.wordsFailed[ tmp ];
                            words.wordsFailedOnly[ counter ][ 'newKey' ] = tmp;

                        }

                    });

                });

In other words, with console.log(words.wordsFailed) I notice that both objects have been assigned the "newKey" key. This is a very add behavior, in my code I just create newKey for the target, at no moment I have told to add this key to the source object. I just can't understand what is going on. How can I avoid this behavior ? I need the newKey only in the target object.

Can someone help please if you have an idea?

EDIT for additional information:

Example of what the source target contains:

{deu: 3, id: 16, eng: 3}

Example of what the target contains after it has gone through the foreach loop (this is the expected result):

{deu: 3, id: 16, eng: 3, newKey: 10}

On the second foreach loop the source also contains the "newKey" key (this is not what I expected. I don't expect the source to have the "newKey" key):

{deu: 3, id: 16, eng: 3, newKey: 10}

Expected result for target:

{deu: 3, id: 16, eng: 3, newKey: 10}

Expected result for the source:

{deu: 3, id: 16, eng: 3}
  • Assigning an object value to another variable or property does **not** make a copy. You assign a reference to the same single object. – Pointy Nov 28 '20 at 15:01
  • **problem:** https://stackoverflow.com/questions/29050004/ - https://stackoverflow.com/q/18359093/9867451, **solutions:** https://stackoverflow.com/questions/12690107/ - https://stackoverflow.com/q/728360/ - https://stackoverflow.com/q/122102/, ... – ibrahim mahrir Nov 28 '20 at 15:10
  • `Let me apologize for my poor english` followed by a Harry Potter book – Jeremy Thille Nov 28 '20 at 15:22

1 Answers1

1

This is not strange behavior, although it can certainly can seem strange when you're first starting out. What's happening is that when you assign words.wordsFailed[tmp] to words.wordsFailedOnly[counter], you're not copying the data, you're referencing the original object. When you mutate that object (add a new key/value pair), you're only adding it to one object, but you have two places pointing to that object: words.wordsFailed[tmp] and words.wordsFailedOnly[counter].

// In javascript, just because two objects have the same key/values, doesn't mean they are equal
const a = { foo: 1 }
const b = { foo: 1 }

a === b // false

// but if you assign an existing object to a new variable, it doesn't copy the object, so the reference is the same, and the equality test passes. They're equal because they are the same instance in memory.
const a = { foo: 1 }
const b = a
a === b // true

// The same thing is happening with your code
const obj = words.wordsFailed[tmp] // just for example
words.wordsFailedOnly[counter] = words.wordsFailed[tmp];
obj === words.wordsFailedOnly[counter] // true
obj === words.wordsFailed[tmp] // true
words.wordsFailedOnly[counter] === words.wordsFailed[tmp] // true

So, any time you update one, you update the "other", because there is no other object here, just the one you started with.

OK, so how do you fix this? You need to create a copy of the object. You can do that many ways, but in modern JavaScript, the simplest is as follows, but please not this only works for flat objects (one level of key-value pairs, no nesting).

// using the spread operator syntax
words.wordsFailedOnly[counter] = { ...words.wordsFailed[tmp] }

// or you can do this
words.wordsFailedOnly[counter] = Object.assign({}, words.wordsFailed[tmp])

Now you've made a copy of your source object, and when you add new keys to the target object, it won't update the source, because they are now to different objects in memory.

There's actually a really good series (by email only unfortunately) that you can check out here. It goes into great depth about this very issue. It's one of the best explanations of how this works in JavaScript.

Matthew Brooks
  • 521
  • 2
  • 5