Unexpected behavior using jQuery.extend with custom objects as attributes - javascript

I am using jQuery (1.11.2) and $.extend to clone an object. This doesn't work as expected.
I have a source object containing two attributes with custom instances of functions (new function...) as values. These values seems to be copied as references where I expect them to be copied as values. See the code below.
var AnyObj = function(){
this.attribute = "anyAttribute";
};
var target = {
a: new AnyObj(),
b: new AnyObj()
}
//this prints "anyAttribute"
console.log(target.a)
//now I create a deep copy
var copy = $.extend(true, {},target);
//and I change the attribute of the COPY(!!!)
copy.a.attribute = "YAYA";
//this prints an object with the value YAYA
console.log(copy.a)
//this should NOT print an object with the value YAYA
console.log(target.a)

It seems this is a jQuery extend() bug. Refer to below for more details:
http://bugs.jquery.com/ticket/10014

Related

when changing value of copied variable it is also changing main variable value in angularjs

I am in a strange condition. I have an array of objects, I used angular.forEach to modify each object price key value but when I am changing it in each it is also changing main array object as well.
Have a look on code, you will understand then what I am trying to say.
var option_1_val = $scope.options.option_1_val;
var option_2_val = $scope.options.option_2_val;
console.log('genies',sc.genies);
var new_arr = [];
var each ;
each = sc.genies;
angular.forEach(each,function(val,key){
var ob = {};
ob = val;
var priceA = angular.fromJson(ob.price);
console.log('price',priceA);
var option = option_1_val.replace(" ","-")+","+option_2_val.replace(" ","-");
console.log(option);
ob.price = priceA[option];
console.log(ob);
new_arr.push(ob);
});
option = 'Non-Vegetarian,' (after calculating)
sc.genies = [{"gs_id":"3","user_id":"25","service_id":"7","price":"{\"Vegetarian,Bengali\":\"200\",\"Vegetarian
,Chinese\":\"3100\",\"Vegetarian,Gujarati\":\"800\",\"Vegetarian,Italian\":\"100\",\"Vegetarian,Maharashtrian
\":\"100\",\"Vegetarian,Punjabi\":\"100\",\"Vegetarian,-South-Indian\":\"300\",\"Vegetarian,Thai\":\"100
\",\"Non-Vegetarian,Bengali\":\"1100\",\"Non-Vegetarian,Chinese\":\"3100\",\"Non-Vegetarian,Gujarati
\":\"100\",\"Non-Vegetarian,Italian\":\"100\",\"Non-Vegetarian,Maharashtrian\":\"100\",\"Non-Vegetarian
,Punjabi\":\"100\",\"Non-Vegetarian,-South-Indian\":\"80\",\"Non-Vegetarian,Thai\":\"100\",\"Jain,Bengali
\":\"2100\",\"Jain,Chinese\":\"2100\",\"Jain,Gujarati\":\"4100\",\"Jain,Italian\":\"100\",\"Jain,Maharashtrian
\":\"100\",\"Jain,Punjabi\":\"100\",\"Jain,-South-Indian\":\"800\",\"Jain,Thai\":\"100\"}","min_price"
:"80","max_price":"4100","username":"abdul quadir","email":"abdul.quadir#kiozen.com","rating":"3"}]
now when I am repeating sc.genie, I have taken it in a new variable already "each" and then I am changing "price" key of each array to undefined but strange point is when I see in console value of price in sc.genies is also changed to "undefined". Huh!
I hope you got my point, please help me why is that happening.
Thanks
You should use angular.copy then when change in each value not affect to original value. because of angular.copy assign old value in new variable without reference.
like:
var each ;
each = angular.copy(sc.genies);
instead of
each = sc.genies;
There is a simple answer. The reason why "both" values changes, is because it is actually the same object. The variable val from this line angular.forEach(each,function(val,key){ ... contains a pointer to an object. It is not another object. It is the same object, it is only accessed via different variable name.
If you really want the original and working copy to be different objects, then you need to manually create new instance with the same values.
You can create copy of an object like this (good for simple objects):
var copy = JSON.parse(JSON.stringify(originalObject));
or as pointed in the comment above, you can use angular.copy(source, destination). See the documentation https://docs.angularjs.org/api/ng/function/angular.copy

AngularJS: How to avoid copying reference from one scope to another?

I have following code where i need to create a single instance which have been updated and saved without any change on the previous scope.
console.log("Trying to save playlist");
console.log($rootScope.selectedSounds);
$scope.playlistInstance = $rootScope.selectedSounds;
var preparedSoundsForSave = [];
angular.forEach($scope.playlistInstance, function (selectedSound, key){
console.log("Sound to playlist is following:");
console.log(selectedSound);
var selectedSoundInstance = selectedSound;
selectedSoundInstance.state = 0;
delete selectedSoundInstance.mediaInstance ;
preparedSoundsForSave.push(selectedSoundInstance);
});
// Create the new object which has been saved
$scope.playlist.name = $scope.playlistName;
$scope.playlist.tracks = preparedSoundsForSave;
console.log($scope.playlist);
existingPlaylists.push($scope.playlist);
localStorageService.set("playlists", existingPlaylists);
$ionicLoading.show({
duration: 1000,
template: $translate.instant('SAVED')
});
$scope.playlistName = "";
So i created single instance, changed some values and saved changed array.
Problem is, that changes are automatically passed to the:
$rootScope.selectedSounds
Without any reason.
I would like to ask, how can i do it in AngularJS correctly please?
Many thanks for any advice.
In javascript objects are passed by reference. So when you are copying the object in a different variable the reference remains the same hence any changes to any one will apply to both the object.
To create a copy with a different reference you can use
// Shallow copy
var newObject = jQuery.extend({}, oldObject);
// Deep copy
var newObject = jQuery.extend(true, {}, oldObject);
Or by using loadash or underscore like
_.extend({},oldObj)

Clone array of objects fails

I fetch some fancy data from an MVC Controller and asign it to the global variable "data_Visits". Then, later in the party, I need to operate repeatedly on the original data in "data_Visits".
To not change values in "data_Visits", I thought to clone it and then operate on the clone. Still, the following seems to change values in "data_Visits":
var data = data_Visits.slice(0);
data.forEach(function (d) {
d.date = new Date(ToJavaScriptDate(d.date));
d.result1 = +d.result1;
d.result2 = +d.result2;
});
Would anybody happen to know WHY?
Because you're making a clone of an array of references. You need to clone each array entry specifically, otherwise both arrays would contain the different collections of references to the same objects.
What you need to do is called a deep copy.
As soon as you've specified jquery tag - here is an example:
var data = $.extend(true, [], data_Visits);
References:
http://api.jquery.com/jQuery.extend/
PS: as a simple example:
This is what you basically do:
var a = { foo: 'bar' };
var b = a;
and even though you have 2 variables - they refer to the same object.
I agree, extend is what you want. If you use an array - you can use slice.
var d = {bob: 'yes'}
var b = jQuery.extend({}, d);
b.bob = 'no'
// shows b modified, d is not
console.log(b, d);
here is a nice referencde:
How do I correctly clone a JavaScript object?

Pointer behavior between objects

I would like to understand well something i observe more and more.
In some circonstances, different instances from a same model change their attributes the same way (if i have 2 UserModel A and B, if i change A, B will be affected the same way).
I observed some practical cases:
When i send an instance of a model to a view constructor, they are linked, if i change the model in my view, the outside one will be affected the same way. I guess sometime we just send a pointer and not a copy of the instance to the view.
More specific with some code;
A = new UserModel();
B = new UserModel();
var Data = A.get('info'); //where info = {some: "thing"};
Data.some = 'other';
B.set('info', Data); //A.get('info') == B.get('info')
Because i got the object info and not only the attributes separately (i tested it and there is no repercution between the values this way).
So my question is, are we always using pointers with objects in javascript ? Is it specific to backbone ? I would like to understand what is behind this behavior.
Thanks.
Objects and Arrays are passed or assigned as references in javascript, not copies. If you want a copy of an object or an array, you have to explicity make a copy.
Simpler types such as numbers, boolean are copied when assigned or passed.
Strings are a bit of special case. They are passed as references, but since strings are immutable (can't be changed), you can't really use a string reference for anything because any attempt to modify the string creates a new string.
A couple examples:
// arrays assigned by reference
var a = [1,2,3];
var b = a;
a[0] = 0;
alert(b[0]); // alerts 0 because b and a are the same array
// objects assigned by reference
var c = {greeting: "hello"};
var d = c;
c.greeting = "bye";
alert(d.greeting); // alerts "bye" because c and d are the same object
// numbers assigned as copies
var e = 3.414;
var f = e;
e = 999;
alert(f); // alerts 3.414 because f is its own copy of the original number
// make a copy of an array
var g = [1,2,3];
var h = g.slice(0); // h is now a copy
h[0] = 9;
alert(g); // shows [1,2,3]
alert(h); // shows [9,2,3]
The same is true for passing arguments to a function or returning values from a function. Unless an explicit copy is created, arrays and objects are passed or returned by reference.
A shallow copy of an array can be made with the .slice() method.
var arr1 = [1,2,3];
var arr2 = arr1.slice(0); // make independent copy of first array
A shallow copy of an object can be made by copying each property from the original object to a new object.
Deep copies involve testing the type of each item being copied and recursing on the object if it is an object or array so nested objects and arrays are copied too.

Replace a one javascript object with another object

On page load I am creating two Javascript Objects, objDemo1 and objDemo1Backup where the latter is simply an exact copy of the first.
e.g.
objDemo1 {
sub_1 = { something: 123, somethingElse: 321 },
sub_2 = { something: 456, somethingElse: 654 }
}
I can modify the values in sub_ as well as add / delete new sub_'s but the only object I am editing is objDemo1. i.e. I never change objDemo1Backup
I have a reset button that when clicked will reset objDemo1 back to what it was when the page originally loaded (i.e. objDemo1 = objDemo1Backup). This is where I am having the issue..
How do I set objDemo1 to objDemo1Backup?
I have tried:
objDemo1 = objDemo1Backup;
and
objDemo1 = null;
var objDemo1 = objDemo1Backup;
...as well as similar variations but nothing seems to work.
Any ideas?
Note: I can confirm that at the point of resetting, objDemo1Backup is exactly the same as it was when I created it and objDemo1 has changed.
My code is definetly hitting the "reset" functionality, where I've tried the objDemo1 = objDemo1Backup... I just cannot figure out the syntax to replace the object.
I'm using angularjs and it took me some time to find out how to copy an object to another object. Normally you'll get an objects clone by calling clone or here in angular copy:
var targetObj = angular.copy(sourceObj);
This gives you a new cloned instance (with a new reference) of the source object. But a quick look into the docs reveals the second parameter of copy:
angular.copy(sourceObj, targetObj)
This way you can override a target object with the fields and methods of the source and also keep the target objects reference.
In JavaScript objects are passed by reference, never by value. So:
var objDemo, objDemoBackup;
objDemo = {
sub_1: "foo";
};
objDemoBackup = objDemo;
objDemo.sub_2 = "bar";
console.log(objDemoBackup.sub_2); // "bar"
To get a copy, you must use a copy function. JavaScript doesn't have one natively but here is a clone implementation: How do I correctly clone a JavaScript object?
var objDemo, objDemoBackup;
objDemo = {
sub_1: "foo";
};
objDemoBackup = clone(objDemo);
objDemo.sub_2 = "bar";
console.log(objDemoBackup.sub_2); // undefined
You can use Object.assign.
ObjectConstructor.assign(target: T, source: U): T & U
It takes up two parameters: target and source. When function completes, all internals of target object will be appended with source one.

Categories