The following is an example scenario I made up for my own practice of this problem. If you want to skip straight to the technical details, please see 'Technical Details' below.
I have a personal project I've been working on to learn JavaScript. Basically, the user can design a shoe by picking available options.
The trick is that the left and right shoe must have the same size, among other properties, but things like color, shoe lace texture, etc. can be independent properties per shoe. (I figured this was a decent way for me to practice object manipulation and inheritance).
The user starts off with designing the right shoe; when the "swap" button is clicked to look at the left shoe, the user currently sees a copy of the right shoe (but inverted). Only on the first swapping of shoes is the left shoe generated and made an exact copy of the right shoe. From then onwards, unique options per shoe orientation are preserved. Then, if the user makes specific changes to that left-shoe model, and then switches to the right shoe, the user is supposed to see the exact same right shoe that they had originally designed before they clicked the "swap" button.
So if their right shoe had red laces, they switch to the left shoe view and make the left shoe have a blue lace, then when switching back to the right shoe the user should see red laces!
Technical Details
When I was writing the code for my main project I was running into trouble with the unique options being perserved. For example, if I made the laces green for the left shoe, the right shoe would always have green laces. Troubleshooting down to the problem was easy because the only time the right shoe was losing it's unique options, such as a red lace, was when I would set the lace color for the left shoe.
console.log("THE RIGHT LACE BEFORE: " + rightShoe.laceId);
leftShoe.laceId = 'green';
console.log("THE RIGHT LACE AFTER: " + rightShoe.laceId);
What would log to the console was:
THE RIGHT LACE BEFORE: red
THE RIGHT LACE AFTER: green
Even though I wasn't changing the rightShoe
, it was being changed whenever I changed the leftShoe
property.
So I went back to where I first define the leftShoe
object, which is when the user clicks "swap" for the first time in the life of the script (My amateur thought was why propagate and fill the leftShoe
object if the user possibly never customizes the left shoe? Maybe it's being unnecessarily stingy with data, I don't know). From then onward, I never redefined the leftShoe
to be a copy of rightShoe
or vice versa. I figured that I was getting hung up by the fact that I was probably doing object referencing and, just like with other languages, I was changing the reference and not the value.
Before coming to SO with my troubles, I wanted to make a JSFiddle to recreate the problem. Being that my project is lengthy (around ~1500 lines, including some THREE.js for graphics), I did my best to emulate the process. And so here it is.
Except the JSFiddle worked exactly as I expected it to! The left model preserved it's unique attribute and data set to that attribute. So, I did a little more digging and read about the Object.assign() method. So in my original project code (not the fiddle), I changed this:
leftShoe = rightShoe;
to this:
leftShoe = Object.assign({}, rightShoe);
As excited as I am to have finally gotten this to work, I am equally amused and perplexed because I don't understand why my JSFiddle didn't need the assign()
method but my identical project code did. Thank you.
The following is an example scenario I made up for my own practice of this problem. If you want to skip straight to the technical details, please see 'Technical Details' below.
I have a personal project I've been working on to learn JavaScript. Basically, the user can design a shoe by picking available options.
The trick is that the left and right shoe must have the same size, among other properties, but things like color, shoe lace texture, etc. can be independent properties per shoe. (I figured this was a decent way for me to practice object manipulation and inheritance).
The user starts off with designing the right shoe; when the "swap" button is clicked to look at the left shoe, the user currently sees a copy of the right shoe (but inverted). Only on the first swapping of shoes is the left shoe generated and made an exact copy of the right shoe. From then onwards, unique options per shoe orientation are preserved. Then, if the user makes specific changes to that left-shoe model, and then switches to the right shoe, the user is supposed to see the exact same right shoe that they had originally designed before they clicked the "swap" button.
So if their right shoe had red laces, they switch to the left shoe view and make the left shoe have a blue lace, then when switching back to the right shoe the user should see red laces!
Technical Details
When I was writing the code for my main project I was running into trouble with the unique options being perserved. For example, if I made the laces green for the left shoe, the right shoe would always have green laces. Troubleshooting down to the problem was easy because the only time the right shoe was losing it's unique options, such as a red lace, was when I would set the lace color for the left shoe.
console.log("THE RIGHT LACE BEFORE: " + rightShoe.laceId);
leftShoe.laceId = 'green';
console.log("THE RIGHT LACE AFTER: " + rightShoe.laceId);
What would log to the console was:
THE RIGHT LACE BEFORE: red
THE RIGHT LACE AFTER: green
Even though I wasn't changing the rightShoe
, it was being changed whenever I changed the leftShoe
property.
So I went back to where I first define the leftShoe
object, which is when the user clicks "swap" for the first time in the life of the script (My amateur thought was why propagate and fill the leftShoe
object if the user possibly never customizes the left shoe? Maybe it's being unnecessarily stingy with data, I don't know). From then onward, I never redefined the leftShoe
to be a copy of rightShoe
or vice versa. I figured that I was getting hung up by the fact that I was probably doing object referencing and, just like with other languages, I was changing the reference and not the value.
Before coming to SO with my troubles, I wanted to make a JSFiddle to recreate the problem. Being that my project is lengthy (around ~1500 lines, including some THREE.js for graphics), I did my best to emulate the process. And so here it is.
Except the JSFiddle worked exactly as I expected it to! The left model preserved it's unique attribute and data set to that attribute. So, I did a little more digging and read about the Object.assign() method. So in my original project code (not the fiddle), I changed this:
leftShoe = rightShoe;
to this:
leftShoe = Object.assign({}, rightShoe);
As excited as I am to have finally gotten this to work, I am equally amused and perplexed because I don't understand why my JSFiddle didn't need the assign()
method but my identical project code did. Thank you.
1 Answer
Reset to default 19Object.Assign makes a new COPY of the left shoe, including all enumerable OWN properties. When you use leftshoe = rightshoe, you are making a REFERENCE to the left shoe. A reference points to the same object, not a NEW COPY. Thus changing a property of the reference is actually changing the original, as you noted.
{}
) and copy all properties usingObject.assign
. Mutating one object won't reflect in the other. – Bergi Commented Apr 19, 2016 at 21:54console.log
s that ended up with the same value. Doesn't make a difference. Regardless, I just don't see what is behaving unexpected in that fiddle. Maybe put the output you're getting in your question, and point to the line where you expected something else. – Bergi Commented Apr 19, 2016 at 22:06leftChoice.id
be 444 right after you have set it to that value? Of course,rightChoice.id
will now give 444 as well, since they still point to the same object. – Bergi Commented Apr 19, 2016 at 22:10rightChoice
that points to the same object) before logging it. – Bergi Commented Apr 19, 2016 at 22:12