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.
Related
This question already has answers here:
Copy array by value
(39 answers)
Copy array of objects and make changes without modifying original array
(5 answers)
Closed 10 months ago.
I was studying JS when faced an unexpected behavior by me. It's probably something related to local vs global scope, however I could not find the answer. Here how it goes:
I have two arrays, one empty and another with some values. If a push the one with values inside the empty one, then updating the values using .push() method, it will update the array inside the other as well:
let newArr = [];
let valueArr = [0,0];
newArr.push(valueArr);
// newArr = [[0,0]]
console.log(newArr);
valueArr.push(0);
// newArr = [0,0,0] --> updates it
console.log(valueArr);
However, if a now try to update the variable using = assignment, it won't change the array inside newArr
let newArr = [];
let valueArr = [0,0];
newArr.push(valueArr);
// newArr = [[0,0]]
console.log(newArr);
valueArr = [0,0,0];
// newArr = [[0,0]] --> does not update it
console.log(newArr);
Does anyone know why pushing has this behavior and reassigning does not? It's due local/global scope or when I reassign the valueArr variable, the newArr variable loses its track?
This is not related to local/global scope.
TLDR
When you call push on an array, you add items to the existing array. When you use an assignment, you create a new array that has nothing to do with the one that was stored in the variable previously.
Detailed
Here's what's happening in the code:
Step 1. let valueArr = [0,0];
When you assign an array to a variable, there are two things happening:
An array is getting created and saved in memory([0,0])
The valueArr variable saves a reference to the created array
Step 2. newArr.push(valueArr);
Then, you push valueArr array into the newArr array. Now newArr[0] contains an element with the same reference as valueArr. They both point to the [0,0] array.
Step 3. valueArr.push(0);
In this step, we push an element to the array that valueArr references. Since newArr references the same array, newArr[0] gets updated too.
Step 4. valueArr = [0,0,0];
Here we repeat step 1. Create a new array and save its reference to the valueArr. The thing is that we change the reference stored in the valueArr, but newArr[0] still points to the old array. This is why you don't see any updates: it's a completely different array.
This would be a good start if you want to learn more about reference types: https://codeburst.io/explaining-value-vs-reference-in-javascript-647a975e12a0
TLDR
If you want to update newArr, do so explicitly:
valueArr = [0,0,0];
newArr[0] = valueArr;
In depth explanation
You need to distinguish between an object and a reference to object. An object is a "thing". But a reference to an object is just a way to describe which "thing" you are talking about.
A variable is a way to explicitly reference an object using a name. But you can also reference objects in other ways, such as (as you discovered) through an array index.
A couple of important things to keep clear:
The assignment(=) operator explicitly creates a reference to an object. You can later refer to that object through the variable name.
A variable can refer to different objects over time (through repeated applications of the assignment operator).
Arrays are JavaScript objects. You create a new array object every time you use square brackets ([ ]) on the right hand side of the assignment operator.
a.push(b) implicitly creates a reference from some index of a to the object being referenced by b at that point in time.
You can use the strict equality(===) operator to determine whether two expressions refer to the same object.
/*
We create a new array, lets call it Object 1.
We can refer to that array using the variable name `a`.
*/
let a = [];
/*
We create a new array, lets call it Object 2.
We refer to that array using the variable name `b`.
*/
let b = [0];
/*
When we call `push` we are implicitly creating a
new reference: from a[0] to Object 2.
*/
a.push(b);
/*
`a[0]` and `b` are both referring to
the *exact same* object: Object 2.
*/
console.log("Are `a[0]` and `b` the same object:", a[0] === b); // -> true
/*
We create a new array, lets call it Object 3.
We explicitly tell Javascript to use `b` to
refer to Object 3 from this point forward.
*/
b = [2];
/*
Notice that `a[0]` still refers to Object 2.
*/
console.log("Are `a[0]` and `b` the same object:", a[0] === b); // -> false
/*
We can explicitly tell javascript to update `a` by
using the assignment operator.
*/
a[0] = b;
console.log("Are `a[0]` and `b` the same object:", a[0] === b); // -> true
Consider this tiny bit of javascript code:
var a = [1, 2, 3],
b = a;
b[1] = 3;
a; // a === [1, 3, 3] wtf!?
Why does "a" change when I update "b[1]"? I've tested it in Firefox and Chrome. This doesn't happen to a simple number for example. Is this the expected behaviour?
var a = 1,
b = a;
b = 3;
a; // a === 1 phew!
It's the same array (since it's an object, it's the same reference), you need to create a copy to manipulate them separately using .slice() (which creates a new array with the elements at the first level copied over), like this:
var a = [1, 2, 3],
b = a.slice();
b[1] = 3;
a; // a === [1, 2, 3]
Because "a" and "b" reference the same array. There aren't two of them; assigning the value of "a" to "b" assigns the reference to the array, not a copy of the array.
When you assign numbers, you're dealing with primitive types. Even on a Number instance there's no method to update the value.
You can see the same "they're pointing to the same object" behavior with Date instances:
var d1 = new Date(), d2 = d1;
d1.setMonth(11); d1.setDate(25);
alert(d2.toString()); // alerts Christmas day
In addition to the other answers, if you want a copy of an array, one approach is to use the slice method:
var b = a.slice(0)
All Javascript objects are passed by reference - you would need to copy the entire object, not assign it.
For arrays this would be simple - just do something like:
var a = [1, 2, 3];
var b = a.slice(0);
where slice(0) returns the array from offset 0 to the end of the array.
This link has more info.
There is a difference between Value types and Reference types.
Basicly Value types are stored on the computers "Stack" directly which means that you actualy have the "Value" and not what is called a Pointer/Reference.
A Reference Type on the other hand, does not hold the actual value of the object/variable but instead it points ( references ) to where you can find the value.
Thus when you say "My Reference Type B points to what Reference Type A points to", you actually have two variables pointing in the same direction and when you interact with either of them and change something, both will be pointing to the changed place, since the pointed to the same place from the begining.
In your other case you say "Hey, copy the value in variable A to variable B" and thus you have the values on seperate places which means that any change to either of them will not affect the other.
Because arrays are references to actual place where they are stored.
the statement x = y is the only special one here, it means "point the variable x at the object y.
Pretty much every other operation is an operation which changes the object referred to by x
b = a;
b[1] = 3;
So, your first statement points the variable b at the array also referred to as a
Your second statement alters the array pointed to by b (and also by a)
Assigning an array does not create a new array, it's just a reference to the same array. Numbers and booleans are copied when assigned to a new variable.
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'm having an issue with saving a variable value at a certain time in javascript. At a basic level, in the code below, I would like variable 'b' to keep the value it was assigned
var a = [];
var b = '';
var c = 'value';
a.push(c);
b = a;
console.log(b); // b = ["value"]
a.push(c);
console.log(b); // b = ["value", "value"], but i want it to be just ["value"]
I've seen various solutions to similar problems using closures such as in this question: Intentionally "freezing" a javascript variable using a self-executing function. I have tried that solution unsuccessfully in this Jsbin: http://jsbin.com/zusara/1/edit?js,console.
Any help would be greatly appreciated.
Thanks and best!
Assigning an array to a variable does NOT make a copy. Thus both a and b variables then point to the same array and any change made via either variable will show up in the other.
If you want a copy of an array in Javascript, then you have to explicitly make a copy of the array.
var a = [];
var c = 'value';
a.push(c);
// make shallow copy of a into b
var b = a.slice(0);
b now contains a completely separate array and modifications of a will not affect b.
Note: this is a shallow copy, not a deep copy and is the solution for arrays. A shallow copy doesn't not copy objects in the array or objects in those objects. Making a deep copy requires substantially more code, but is often not required and does not appear to be required in your case for the example you provided.
If you want a deep copy and want to include objects too, not just arrays (I provided the simple solution for a shallow copy of an array), you can see this reference there are plenty of options and debate here:
What is the most efficient way to deep clone an object in JavaScript?
One way to achieve this is by using the slice function.
Instead of:
b = a;
Try using:
b = a.slice();
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.