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.
Related
I am having trouble maintaining the original value of a variable after making new changes to the original variable.
Code:
(...)
data = Illumination.calculate_N(data)
data = Illumination.calculate_pi(data)
data = Illumination.calculate_kwh(data)
data = Illumination.calculate_ca(data)
let data_base = data
let ca_base = data.ca
let kwh_base = data.kwh
let pi_base = data.pi
(...)
data = Illumination.calculate_N(data)
data = Illumination.calculate_pi(data)
data = Illumination.calculate_kwh(data)
data = Illumination.calculate_ca(data)
let data_proposto = data
let ca_proposto = data.ca
let kwh_proposto = data.kwh
let pi_proposto = data.pi
-----------------------------------
EXAMPLE:
static calculate_ai(data){
data.ai = data.areaTotal*data.au
return data
}
It was expected that the original variable (date) would have its values changed, and this happens correctly, however, the variables data_base and data_proposto are not keeping their values
Both variables at the end of the calculation have the same values as the variable date
The variables ca_proposto, ca_base, and the like store their values correctly
Any idea?
The only interactions of the variables data_base and data_proposto were their creations with the data variable and their return of the function
OBS: If I use console.log () to view the value of the data_base variable before redoing the new calculations (Illumination.calculate_N (data)), the value of the variable appears correctly as it should, it is changed shortly after these calculations.
Because in both cases you are assigning not the object itself in the current state, but a reference to that object. What you need to do is to clone the object so the state is frozen at that point.
Simple Clone (Shallow Copy)
let data_base = Object.assign({}, data); //you get a clone of data
let data_proposto = Object.assign({}, data);
The limitation here is that it only does a shallow copy. See Deep Copy below for further explanation.
JSON Clone
This is a quick-and-dirty way to clone as it converts a JSON object to a string, and then back. i.e. you are no longer getting a reference, but a new object.
let data_base = JSON.parse(JSON.stringify(data));
let data_postero = JSON.parse(JSON.stringify(data));
But this won't work if your object is not JSON-safe.
Deep Copy
The least elegant method is probably safest. It deep copies the properties over into a new object. The key difference with Object.assign() is that it copies the values of nested properties, whereas Object.assign() copies the reference to nested objects.
So with Object.assign() any subsequent changes in your nested objects will affect all versions of your "clones". This won't happen if your clones only have property values of those nested objects at the time of cloning – these values are not affected by any changes to the nested objects.
const deepCopy = function(src) {
let target = {};
// using for/in on object also returns prototype properties
for (let prop in src) {
// .hasOwnProperty() filters out these prototype properties.
if (src.hasOwnProperty(prop)) {
target[prop] = src[prop]; //iteratively copies over values, not references
}
}
return target;
}
let data_base = deepCopy(data);
let data_postero = deepCopy(data);
#chatnoir Defined the problem very well, But I do not agree with his JSON serialization solution due to the below probleam:
You will lose any Javascript property that has no equivalent type in
JSON, like Function or Infinity. Any property that’s assigned to
undefined will be ignored by JSON.stringify, causing them to be missed
on the cloned object.
My suggestion to perform deep copy is to rely on a library that’s well
tested, very popular and carefully maintained: Lodash.
Lodash offers the very convenient clone and deepclone functions to perform shallow and deep cloning.
Lodash has this nice feature: you can import single functions separately in your project to reduce a lot the size of the dependency.
Please find the running sample code here: https://glitch.com/edit/#!/flavio-lodash-clone-shallow-deep?path=server.js:1:0
You are using the same variable data inside and outside functions.
ie; data is in the global scope.
static calculate_ai(data){
data.ai = data.areaTotal*data.au
return data
}
even though you are expecting the scope of the variable data inside the method calculate_ai to be limited to that method, it is not the case. data is in global scope and therefore, the value changes inside the method for the variable affects outside as well.
An effective solution is to use a different variable inside the method.
A variable is like an octopus tentacle, and not as a box (as it’s commonly described). In this analogy, the variable's name can be thought of as the name of a tentacle.
A variable (tentacle) holds on to a value in what’s called a binding. A binding is an association of a variable to a value: x = 1.
In JavaScript, if a variable b holds on to variable a, changing the value to which variable a holds onto, will change the value to which variable b holds onto, as b and a are referencing to the same value:
let a = {key: 1}
let b = a
console.log(`a: ${a.key}`) // -> 1
console.log(`b: ${b.key}`) // -> 1
a.key = 2
console.log(`a: ${a.key}`) // -> 2
console.log(`b: ${b.key}`) // -> 2
a = {key: 3} // This will point variable 'a' to a new object, while variable 'b' still points to the original object.
console.log(`a: ${a.key}`) // -> 3
console.log(`b: ${b.key}`) // -> 2
Strings and arrays behave differently when altering a variable which was made from reference.
I'm curious why this is, as it confuses me greatly.
I was messing around with JavaScript, and noticed something that I don't fully understand.
Let's say I make a variable.
var a = 'Hello';
Then I make a new variable by reference to the "a" variable.
var b = a;
Now I alter the b variable.
b = 'World';
The "a" variable remains "Hello".
console.log( a ) // 'Hello'
However, if I make the "a" variable an array, and once again reference it in the "b" variable, now altering the "b" variable also alters the "a" variable.
var a = [ 'Hey', 'There', 'Buddy' ];
var b = a;
b[1] = 'Where?';
console.log( a ) // [ 'Hey', 'Where?', 'Buddy']
Why do arrays behave differently from strings and other data types in shown example?
Why do arrays behave differently from strings and other data types in
shown example?
This is because string in javascript are passed by value or copied and not reference. Whereas arrays are passed by reference.
Suppose you have:
var a = "Hello":
var b = a;
The value of variable a is been copied to variable b.
But when you do :
var a = [ 'Hey', 'There', 'Buddy' ];
var b = a;
You just pass reference of the array a to b. So both a and b points to the same reference. Therefore changes made in any one of them will reflect in other.
Basically you have something in the memory as:
a:ref12344−−−+
|
|
| +−−−−−−−−−−−−−+
+−−−>| Array |
| +−−−−−−−−−−−−−+
| | String |
| | String |
b :ref12345−−+ | |
| |
+−−−−−−−−−−−−−+
Because b is not a copy, it's a pointer. The array is the same (there is only one instance of it).
One of the way to do a copy of an array is too slice it fully (from its first element at index 0).
var b = a.slice(0);
See also: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice
They behave different, because a String is referenced by value. The Array on the other hand, is a reference by pointer and as such "mirrors" modifications on either of the variables.
If you want to generate a clone of the array, do it by creating a new array:
var old = [1, 2, 3];
var ref = old; // <-- ref still points to "old"
var copy = [...old]; // <-- not a reference any longer
Because you are just pointing to a reference of the array. var b isn't "holding" the array, but it is pointing to where the array was initialized, which is still var a.
In Javascript is passed by value for the primitive types (Number, string ecc...) but it changes when you touch an object (the same for array) it is passed by referece.
var a = 2;
var b = a;
b = b - 1;
console.log(a,b)
var c = {
a: 2
}
var d = c;
d.a = d.a -1;
console.log(c, d)
In JavaScript, arrays and objects are reference types. This means that when a variable is assigned an array or object, what gets assigned to the variable is a reference to the location in memory where the array or object was stored, and not the content of the object/array itself.
However, contrary to some languages, strings are value types in JS and not arrays, they behave just like number, boolean, null and undefined when assigned and copied.
The splice method does not generates a new array, it mutates the array given asargument, so every other variable referencing this same array will also see the change.
If you want your source array to stay unmodifed, use the slice method instead, which is immutable. It will make a copy, mutate this copy and return it.
I am trying to get into Fuse to create mobile apps and they use JavaScript for their logic. I never used JavaScript before and just recently completed their getting started course. Most of the stuff is pretty easy to understand, but I am having trouble with the way they use variables at one point. It would be nice, if somebody could explain how variables behave in JavaScript.
So the problem I have goes as follows:
for (var i = 0; i < hikes.length; i++){
// A new variable gets the value of the array
var hike = hikes[i];
if (hike.id == id){
// The variable gets a new value
hike.name = "foo";
break;
}
}
So, in my understanding of programming, the array hikes should be unchanged and only the variable hike should have foo as the name value. But in reality, the array now also has the name foo.
I guess the variable works as a pointer to the address of the arrays value, but maybe somebody can help me to better understand that concept.
Yes you're right, objects and arrays are always passed as references:
a = {}; // empty object
b = a; // references same object
b.foo = 'bar';
a.foo; // also 'bar'
You can create a deep copy of the array using JSON.parse(JSON.stringify(hikes)); and then use that copied array for manipulation:
var hikes = [
{
'id': 10
}
];
var id = 10;
var tempHikes = JSON.parse(JSON.stringify(hikes));
for (var i = 0; i < tempHikes.length; i++){
// A new variable gets the value of the array
var hike = tempHikes[i];
if (hike.id == id){
// The variable gets a new value
hike.name = "foo";
console.log('hike is ', hike);
break;
}
}
console.log(hikes);
arrays in javascript are passed by reference, whenever you modify an element in an array that change will occur anywhere you are accessing that array, to avoid such issues you have to use Array.from(arg) which creates a new array of from the arg parameter. This also applies to objects, to avoid such issues with objects, you have to use Object.create(obj) to create a new obj of from obj parameter or you can use let newObj = Object.assign( {} , obj ) , whenever you make any modification to the members of newObj the obj object does not see it, in other words there is no direct linkage between this two object, same thing applies for array
Boolean, null, undefined, String, and Number values are called primitive types.
When you assign something that is not a primitive type, namely arrays, functions and objects you are storing a reference to that.
That means that hikes[i] contains a reference to the object, where reference roughly means a pointer to it's location in memory.
When you assign hike = hikes[i] you are copying over the reference and not the actual object. So in fact hike still points to the same object as hikes[i], so any changes to that object are visible on both occasions.
If you want to copy the underlying object, there are different ways of doing so. One of them is Object.assign:
var hike = Object.assign({}, hikes[i])
This is because of pass by reference. All you need to do is create a new object (string, number ...) that you can work on.
for (var i = 0; i < hikes.length; i++){
var hike = hikes.slice(i,i+1)[0];
if (hike.id == id){
hike.name = "foo";
break;
}
}
slice also create a deep copy. you can use splice or assign or ((key1, key2)=>(key1, key2))(obj) etc.
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?
Note: I assume this question has been asked a million times before but I didn't know what to search for.
The question is very simple. Array in javascript seems to be set by reference. How can i set it by value. Meaning, even if I empty the array a, I want array b to be the copy of a i.e., I want alert(b) to alert ['1','2','3']
var a = ['1','2','3'];
var b = [];
b = a;
a.length = 0;
alert(b);
here is a jsfiddle http://jsfiddle.net/e5mQM/
Yes, setting a = b; will make both variables point to the same array / object, so you have to copy the array instead of just referencing it :
b = a.slice();
javascripts slice() method returns a new array sliced based on the parameters given, and if called without parameters it will return a new shallow copy of the entire array.