This is my code and I am trying to change value of object in packs object. But when I type it, Javascript somehow changes all three different objects, that have nothing in common. And this is the only line that changes packs, rest should stay the same, but it's all changing with this line. How?
console.log(packs[usedPack].levels[level].bestBy) //null
console.log(defaultPack.levels[level].bestBy) //null
console.log(mainPacks[usedPack].levels[level].bestBy) //null
packs[usedPack].levels[level].bestBy = nameTyped; //this changes values in three different objects
//packs[usedPack].levels[level] = nameTyped; //if I type like this, this does change only original object, rest stays the same
console.log(packs) //nameTyped
console.log(defaultPack) //nameTyped
console.log(mainPacks) //nameTyped
Edit: It was indeed problem with referencing. I was using constructor function and this function needed to deep copy complex objects. I was checking values and I used multiple loops to copy all values from objects, but it didn't work properly. I've changed all of this to JSON.parse(JSON.stringify()), and it's working now. Thanks for help.
The issue here seems like you're assigning some object by its reference.
In javascript, if you have two objects and you assign them like
a = b;
Now whenever you will change b, a will also be changed. To avoid this we do deep clone using the spread operator
// this now does not reference to b but clones it
a = {...b}
In your code, you might be assigning some objects like this. a=b
Maybe you're assigning packs, defaultPack, and mainPacks using some same object.
Updated
#David pointed out one thing and that is if you are having some complex structure (like objects within object) and then you clone it using spread operator, the inner objects will still reference the same object.
To resolve this, for easiness you can use lodash deepclone function
const clonedeep = require('lodash/clonedeep');
const deepClonedObject = clonedeep(originalObject);
This will deep clone and even if the objects are nested they won't refer to the same object.
Related
I am making a planner/calendar website that is going to have a repeat function.
var chain = _.chain(state.items).filter({'id': 1}).head().value();
console.log(chain);
Here i filter one object, how do i duplicate chain that when i change the original the duplicate also changes and the other way around?
Variables that are assigned a non-primitive value are given a reference to that value. That reference points to the object’s location in memory. The variables don’t actually contain the value. This is why the original value changing when you're changing the duplicate.
This can be solved by using JSON.parse() and JSON.stringify()
Create a new function in the methods section
cloneObject:function(obj){
return JSON.parse(JSON.stringify(obj));
}
Now you can call this method to make a copy of any object, like
var items = this.cloneObject(state.items); // this will create a clone of the object
var chain = _.chain(items).filter({'id': 1}).head().value();
Here the filter won't effect the state.items since we made a clone of this data.
If you're already using lodash JS library, you can use the cloneDeep() method to make the copy
Eg:
var items = _.cloneDeep(state.items);
var chain = _.chain(items).filter({'id': 1}).head().value();
console.log(chain);
Attempting to assign a scope object to a JavaScript variable to do minor manipulation before sending to my API. However, any changes made to the JavaScript variable change the scope object.
var recruitingCallListOutput = $scope.RecrutingCallingList.Recruit;
// manipulation of recruitingCallListOutput
The manipulation actually still updates the scope object which is not desired. Feel I am not understanding something in AngularJS correctly. Is there a way to grab the data and detach it from the scope?
In your example, recruitingCallListOutput is a reference to $scope.RecrutingCallingList.Recruit (see https://codeburst.io/explaining-value-vs-reference-in-javascript-647a975e12a0 for more detail.) You will want to make a copy of $scope.RecrutingCallingList.Recruit.
If Recruit is a shallow object, meaning no nested objects (property values are primitives only), you can simply do
var recruitingCallListOutput = Object.assign({}, $scope.RecrutingCallingList.Recruit);
If you have nested objects/arrays as property values, you'll need to deep copy. It's been a while since I have been in the angular world, but
var recruitingCallListOutput = angular.copy($scope.RecrutingCallingList.Recruit)
you could actually use angular.copy in both examples.
This is nothing to do with AngularJS. It's Javascript, and it's expected behaviour.
For example, if you open the browser console (F12->Console) right now and run this:
var foo = {x:1};
var copy=foo;
copy.x=2;
console.log(foo.x);
you will see {x:2} printed out.
This is the same behaviour you would expected for any object reference in Javascript, C#, Java, etc. Because you are making a reference and not a copy, any changes to the reference are actually changes to the original.
The simplest way to solve this problem in your case is to copy the values you are interested in from the item in question into a totally separate object and modify that copy.
e.g.
var recruitingCallListOutput = {
name: $scope.RecrutingCallingList.Recruit.name,
age:$scope.RecrutingCallingList.Recruit.age,
modifiedSomething: $scope.RecrutingCallingList.Recruit.something + 42 //or whatever modifications you need to make
...and so on.
};
There are ways to "clone" an object in Javascript but unless your object is really really complex I would be careful. And consider if you really need all of the properties of the original object anyway, perhaps you only need to send some of them to your backend.
this.lastLocations[0] = this.locations[0];
this.locations[0].x++;
When this code is executed it increments both locations[0].x and lastLocations[0].x. I want it to just change locations[0].x. Is this because javascript is assigning the reference rather than the value? Or is the problem somewhere else in my code?
Objects in javascript are assigned by reference so both your variables are pointing at the exact same object.
So, when you do this:
this.lastLocations[0] = this.locations[0];
Then, both this.lastLocations[0] and this.locations[0] now point to the exact same object. If you make a change to that object via either one of these variables, then that change will show via the other variable (because they both point at the exact same object).
If you want to assign a copy, then you literally have to make a copy of the object (by creating a new object and then copying over all the properties from the original to the new object) and assign that new copy.
There are numerous other posts on methods for cloning/copying an object:
What is the most efficient way to deep clone an object in JavaScript?
How do I correctly clone a JavaScript object?
is it possible in Javascript to tell an object to stop having reference behavior with another object
And, some other related questions:
Do objects pushed into an array in javascript deep or shallow copy?
Javascript by reference vs. by value
Everything in JavaScript is passed and assigned by value.
"Objects" are not values in JavaScript. The only values in JavaScript are primitives and references (pointers to objects). So array1[0] is either a primitive or pointer to an object, and array2[0] is either a primitive or pointer to an object.
When you do array1[0] = array2[0], it assigns the object pointer so that both pointers point to the same object.
Lets say you have this object:
mainObj = {
foo1: 'bar1',
foo2: 'bar2',
foo3: 'bar3'
}
Now I want to make a close of this object by doing cloneObj = mainOb. Now we have two identical objects.
When I change the value of mainObj.foo1 = 'lolcats' after I made the clone then for some reason cloneObj.foo1 = 'lolcats'
I tested this in Chrome's console on a much more complex object. I know for certain that there's nothing in my script that would keep making the two objects sync up. I even made sure of this by creating random names for the cloneObj.
Is this behavior done on purpose or am I experiencing some sort of bug? Or am I just missing something very fundamental here?
You didn't clone the initial object when you just did cloneObj = mainOb, you are actually passing a reference to mainOb that can be accessed via the cloneObj variable name. You therefore have two variable names referencing the SAME object.
when you assign/clone object such as cloneObj = mainOb you just create another reference to the same object. Both mainOb and cloneOb points to the same object thus, a change in one is reflected on another.
Order.prototype.selectItem = function(newItem) { ...
newItem is the object I want to copy and then modify the copy without modifying the original newItem.
var newSelectedItem = newItem;
newSelectedItem.orderIndex = this.selectedItems.length + 1;
Changing the orderIndex of the copy will also change the original newItem.
Question: How do I copy and then modify the copy without modifying the original newItem.
Thanks!
Create a new object
var newSelectedItem = {};
And copy all properties from the old object into the new one
for(var prop in newItem){
if(newItem.hasOwnProperty(prop)){
newSelectedItem[prop] = newItem[prop];
}
}
Note that this solution only works for plain objects that are flat. If "newItem" is an instance of some prototype you cannot just create the new object with {} and if properties can contain mutable arrays or objects then you also need to do a recursive deep copy.
For more examples, the dojo toolkit has some functions (mixin and clone) for doing this kind of thing: http://trac.dojotoolkit.org/browser/dojo/dojo/trunk/_base/lang.js
If you're using jQuery (which I'd suggest), you can clone objects per this post:
What is the most efficient way to deep clone an object in JavaScript?
You don't necessarily need to clone the object. If all you want to do is have an object you can set properties on without altering the original, the so-called "Boodman/Crockford delegation" is far more efficient. Wishing I had published that when I first used it :) See also dojo.delegate