Related
I've written the following JavaScript:
var myArray = ['a', 'b', 'c'];
var copyOfMyArray = myArray;
copyOfMyArray.splice(0, 1);
alert(myArray); // alerts ['b','c']
alert(copyOfMyArray); // alerts ['b','c']
var myNumber = 5;
var copyOfMyNumber = myNumber;
copyOfMyNumber = copyOfMyNumber - 1;
alert(myNumber); // alerts 5
alert(copyOfMyNumber); // alerts 4
This code declares a variable myArray and sets it to an array value. It then declares a second variable copyOfMyArray and sets it to myArray.
It performs an operation on copyOfMyArray and then alerts both myArray and copyOfMyArray. Somehow, when I perform an operation on copyOfMyArray, it appears that the same operation is performed on myArray.
The code then does the same thing with a number value: It declares a variable myNumber and sets it to a number value. It then declares a second variable copyOfMyNumber and sets it to myNumber. It performs an operation on copyOfMyNumber and then alerts both myNumber and copyOfMyNumber. Here, I get the expected behavior: different values for myNumber and copyOfMyNumber.
What is the difference between an array and a number in JavaScript that it seems changing an array changes the value of a copy of the array, where as changing a number does not change the value of a copy of the number?
I'm guessing that for some reason, the array is referred to by reference and the number by value, but why? How can I know what behavior to expect with other objects?
An array in JavaScript is also an object and variables only hold a reference to an object, not the object itself. Thus both variables have a reference to the same object.
Your comparison with the number example is not correct btw. You assign a new value to copyOfMyNumber. If you assign a new value to copyOfMyArray it will not change myArray either.
You can create a copy of an array using slice [docs]:
var copyOfMyArray = myArray.slice(0);
But note that this only returns a shallow copy, i.e. objects inside the array will not be cloned.
Well, the only possible answer — and the correct one — is that you're not actually copying the array. When you write
var copyOfArray = array;
you're assigning a reference to the same array into another variable. They're both pointing at the same object, in other words.
So everyone here has done a great job of explaining why this is happening - I just wanted to drop a line and let you know how I was able to fix this - pretty easily:
thingArray = ['first_thing', 'second_thing', 'third_thing']
function removeFirstThingAndPreserveArray(){
var copyOfThingArray = [...thingArray]
copyOfThingArray.shift();
return copyOfThingArray;
}
This is using the ... spread syntax.
Spread Syntax Source
EDIT: As to the why of this, and to answer your question:
What is the difference between an array and a number in JavaScript that it seems changing an array changes the value of a copy of the array, where as changing a number does not change the value of a copy of the number?
The answer is that in JavaScript, arrays and objects are mutable, while strings and numbers and other primitives are immutable. When we do an assignment like:
var myArray = ['a', 'b', 'c'];
var copyOfMyArray = myArray;
copyOfMyArray is really just a reference to myArray, not an actual copy.
I would recommend this article, What are immutable and mutable data structures?, to dig deeper into the subject.
MDN Glossary: Mutable
Cloning objects -
A loop / array.push produces a similar result to array.slice(0) or array.clone(). Values are all passed by reference, but since most primitive data types are immutable, subsequent operations produce the desired result - a 'clone'. This is not true of objects and arrays, of course, which allow for modification of the original reference (they are mutable types).
Take the following example:
const originalArray = [1, 'a', false, {foor: 'bar'}]
const newArray = [];
originalArray.forEach((v, i) => {
newArray.push(originalArray[i]);
});
newArray[0] = newArray[0] + 1;
newArray[1] = 'b';
newArray[2] = true;
newArray[3] = Object.assign(newArray[3], {bar: 'foo'});
The operations run on the newArray indices all produce the desired result, except the final (object), which, because it is copied by reference, will mutate the originalArray[3] as well.
https://jsfiddle.net/7ajz2m6w/
Note that array.slice(0) and array.clone() suffers from this same limitation.
One way to solve this is by effectively cloning the object during the push sequence:
originalArray.forEach((v, i) => {
const val = (typeof v === 'object') ? Object.assign({}, v) : v;
newArray.push(val);
});
https://jsfiddle.net/e5hmnjp0/
cheers
The issue with shallow copy is that all the objects aren't cloned, instead it get reference.So array.slice(0) will work fine only with literal array, but it will not do shallow copy with object array. In that case one way is..
var firstArray = [{name: 'foo', id: 121}, {name: 'zoo', id: 321}];
var clonedArray = firstArray.map((_arrayElement) => Object.assign({}, _arrayElement));
console.log(clonedArray);
// [{name: 'foo', id: 121}, {name: 'zoo', id: 321}] // shallow copy
In JS, operator "=" copy the pointer to the memory area of the array.
If you want to copy an array into another you have to use the Clone function.
For integers is different because they are a primitive type.
S.
Create a filter of the original array in the arrayCopy. So that changes to the new array won't affect original array.
var myArray = ['a', 'b', 'c'];
var arrayCopy = myArray.filter(function(f){return f;})
arrayCopy.splice(0, 1);
alert(myArray); // alerts ['a','b','c']
alert(arrayCopy); // alerts ['b','c']
Hope it helps.
Everything is copied by reference except primitive data types (strings and numbers IIRC).
You don't have any copies.
You have multiple variables holding the same array.
Similarly, you have multiple variables holding the same number.
When you write copyOfMyNumber = ..., you're putting a new number into the variable.
That's like writing copyOfMyArray = ....
When you write copyOfMyArray.splice, you're modifying the original array.
That isn't possible with numbers because numbers are immutable and cannot be modified,
You can add some error handling depending on your cases and use something similar to the following function to solve the issue. Please comment for any bugs / issues / efficiency ideas.
function CopyAnArray (ari1) {
var mxx4 = [];
for (var i=0;i<ari1.length;i++) {
var nads2 = [];
for (var j=0;j<ari1[0].length;j++) {
nads2.push(ari1[i][j]);
}
mxx4.push(nads2);
}
return mxx4;
}
An array, or an object in javascript always holds the same reference unless you clone or copy. Here is an exmaple:
http://plnkr.co/edit/Bqvsiddke27w9nLwYhcl?p=preview
// for showing that objects in javascript shares the same reference
var obj = {
"name": "a"
}
var arr = [];
//we push the same object
arr.push(obj);
arr.push(obj);
//if we change the value for one object
arr[0].name = "b";
//the other object also changes
alert(arr[1].name);
For object clone, we can use .clone() in jquery and angular.copy(), these functions will create new object with other reference. If you know more functions to do that, please tell me, thanks!
For Arrays with objects you can change the use JSON.parse and JSON.strinigfy to change the type of the array to an object refrence to a string and then back to a array without having to worry about the original array
var array = [{name:'John', age:34, logs:'[]'}, {name:'David', age:43, logs:'[3]'}];
var array2 = JSON.parse(JSON.stringify(array)); // turn object to function output
array2[0].age++;
alert(JSON.stringify(array));
alert(JSON.stringify(array2));
I've searched about call by reference on javascript but still am confused.
consider this code.
let arr = [];
let temparr = [2,3,4];
arr = temparr;
temparr.push(5);
console.log(arr); //[2,3,4,5]
console.log(temparr); //[2,3,4,5]
let arr2 = [];
let temparr2 = [2,3,4];
arr2 = temparr2;
temparr2 = [1,2];
console.log(arr2); //[2,3,4]
console.log(temparr2); /[1,2]
For the first case, arr gets affected by temparr due to arr = temparr, however, in the second example, arr2 does not get affected by modification in temparr2. My question is,
In the first example, Why in the first place is arr getting affected by the modification of temparr? Is this in the line of call by reference? If so, on what occasions does it trigger such operation?
In the second example, the only difference is that i did not use push but assign new array to modify the temparr2. However this time arr2 did not get affected. What is the difference with the first example?
Thank you very much
Your question has less to do with how arguments are passed (everything is passed by Value all the time in JavaScript, by the way) and more to do with how Objects are stored.
In your first example, the line most relevant to your question is this:
arr = temparr;
Here, you are setting up a second variable to hold the same value as the first, but Object variables don't hold the Object, they hold the location in memory to where the object is stored. So, after that line, you have two variables that both point to the same, one underlying object. If either one of them modifies that underlying object, the other will see the same thing because they both point to only one object.
In your second example, you start out in a similar way with:
arr2 = temparr2;
Which, again, sets you up to have two variables that point to the same one, underlying object. But, then you do this:
temparr2 = [1,2];
Which doesn't modify the one underlying object, it simply reassigns the second variable to a different object, and now the two variables don't point to the same thing anymore.
It's really no different than saying:
x = 7;
and then saying:
x = 8;
The old value in x goes away and there's an entirely new value stored.
If you wanted to modify the underlying array, you would do that via the Array API, for example:
temparr2.splice(2,1); // Delete one item at index position 2
This is why the array was modified when you used .push() in the first example - - you were working on the object, not reassigning the variable.
var arrN = [1, 2, 3];
function init(arr){
arr = [];
console.log(arrN) //output [1, 2, 3], expect []
}
init(arrN);
When using splice or push methods the array passed to a function is being modified. So I am trying to understand what is happening when using assignment operator, why it's not changing the array? is it creating the local var of the passed array?
Any help will be appreciated!
You need to distinguish between the variable and the actual object (the array). splice and push are changing the object.
arr = [] is just changing the variable, the old object just stays as it is.
There is a difference in assigning a different object to a variable or mutating the object currently referenced by a variable.
(Re)assigning
When you do an assignment like:
arr = []; // or any other value
... then the value that arr previously had is not altered. It is just that arr detaches from that previous value and references a new value instead. The original value (if it is an object) lives on, but arr no longer has access to it.
Side note: if no other variable references the previous value any more, the garbage collector will at some point in time free the memory used by it. But this is not your case, since the global variable arrN still references the original value.
Mutating
It is another thing if you don't assign a value to arr, but apply instead a mutation to it, for instance with splice, push, pop, or an assignment to one of its properties, like arr[0] = 1 or arr[1]++. In those cases, arr keeps referencing the same object, and the changes are made to the object it references, which is visible to any other variable that references the same object, like arrN.
Clearing an array
Now if you want to clear the array that is passed to your function, you must avoid to make an assignment like arr = .... Instead, use methods that mutate the array in place:
arr.splice(0)
Or, alternatively:
arr.length = 0;
Now you have actually mutated the array, which is also the array referenced by arrN.
In JavaScript, particularly when working with objects the assignment operator (=) has three jobs.
Assignment
Creating a new reference if the right side is an object.
Breaking any previous referencing and creating multiple assignments if both sides are objects.
Such as;
var a = [1,2,3], // a is assigned an array
b = a; // b is assigned just a reference to a
a = ["a","b","c"]; // b to a referral is now broken
// a is assigned with ["a","b","c"]
// b is assigned with [1,2,3]
same would apply if it was done in reverse order;
var a = [1,2,3], // a is assigned an array
b = a; // b is assigned just a reference to a
b = ["a","b","c"]; // b to a referral is now broken
// b is assigned with ["a","b","c"]
// a keeps it's existing assignment as [1,2,3]
Your are passing arrN to the console instead of passing arr. Also, you just call a function by its name, not by the keyword function. Here is the corrected code:
var arrN = [1, 2, 3];
function init(arr){
arr = [];
console.log(arr)
}
init(arr);
You have also declared init() with an argument arr, which is not needed in this case. What ever the value you pass to init(), the value of arr will be [] because you are reassigning it in the function.
By js convention and as far as I know
Primitive datatypes are worked under pass by value and
complex datatypes are worked under pass by reference
If it so
var ary = [1,2,3];
var dupAry = ary;
//Now I am going to rewrite the ary variable as follows
ary = [3,4,5];
Now I log the values of ary and dupAry it logs different values. By its standard both array should return the vaues.
so why it return different array values?
Another scenario
var ary = [1,2,3];
var dupAry = ary;
//No I gonna apply splice method to the ary.
ary.splice(0,1);
Now both array return same values and it works fine with its standard.
Finally why its doesn't applied with first scenario?
var dupAry = ary;
This assigns a reference to the array that ary points to to dupAry. Both ary and dupAry now hold a reference which points to the same array object. If you now assign something else to ary, then dupAry will continue to hold a reference which points to the array object, and ary will now hold some other value.
By assigning something to a variable, you're not modifying the object that variable already holds; you're modifying the object reference that variable holds.
ary.splice(0,1)
This modifies the actual object that both variables points to, so both see the change.
When you do this:
ary = [3,4,5];
... you aren't altering an array: you are destroying the previous reference and creating a brand new variable* with a brand new array. See the difference with:
ary.push(99);
(*) Well, not really a new variable, of course—I didn't know how to word it.
One of the problems with teaching yourself how to code is that you miss some things that might be fairly simple:
I have written a function which takes text files (e.g. "someData1.txt", "someData2.txt" etc.) turns their contents into an array ("myArray"), and extracts their title as a variable ("fileName").
I am at the stage where I have parsed the filenames and the arrays, but I would like to rename each array with the variable, so that the first array becomes "someData1", and so on. I already have a bunch of code which will do various things with each different array, so it would useful if I could name them the way I wish.
I thought I could use valueOf to do it, as in
fileName.valueOf() = myArray;
but that does not work. So how do I do this?
Thanks!
Probably the easiest way would be to use an object and then use array notation to assign keys to it. Let me show you:
var myObj = {};
myObj.someProperty = 1;
is the same as
myObj['someProperty'] = 1; // note the quotes
So, that makes possible to use variable names as keys. In your example:
var fileName = 'someData1';
var myObj = {};
myObj[fileName] = myArray; // myArray being file contents from your example
and now when you want to access the contents, you can simply do:
myObj.someData1
or:
myObj['someData1']
Just make sure you have no duplicate file names and you're good to go.
What you want to do is defining a variable name dynamically.
It is not possible in Javascript though, but you can be tricky.
You have then two options :
Use the global scope object to store your variables (not a good practice) :
global[variableName] = array;
And then you will be able to access it in the scope :
global['toto'] = 42;
console.log(toto);
=> 42
This is NOT a good practice but is the only way to define a variable in the scope dynamically.
Use an object to store it :
var myArrays = [];
myArrays[variableName] = array;
In each case you define in fact a property of an object.
You have to keep in mind that :
myArrays['toto'] = 42;
is the same that :
myArrays.toto = 42;
So to access your array, just do :
myArrays.toto