I am novice to JavaScript but I know JavaScript does calls by value when passing primitives and calls by reference when passing objects.
var a = [4,5];
var temp = a;
console.log(a); // [4,5] OK
console.log(temp); // [4,5] OK
temp = temp.push(6); // Insert one more element into temp array
console.log(a); // [4,5,6] why so??
Console.log(temp); // [4,5,6]
I know this is because of reference got change . What should I do to avoid deep copy ?
If I change temp don’t want to change original reference a, it should print [4,5].
If you want to copy it, you can use slice:
var temp = a.slice(0);
Edit: thanks to cuberto for pointing me out slice(0)
"I know JavaScript behavior is call by value in primitive value and
call by reference in case of object."
Not exactly. In Javascript everything is passed by value all the time. If you pass an object, you actually pass a reference to the object, and that reference is passed by value.
Example:
var o = { name: 'first' };
// pass object reference to a function
use(o);
function use(x) {
// change the reference
x = { name: 'second' };
}
alert(o.name); // shows "first"
As the reference is passed by value to the function, changing the reference inside the function doesn't change the variable that was used to send the reference.
In your code you have only one array:
var temp = a; // copy the reference from a to temp
You have two variables that reference the same array, so any changes that you do to the array using one variable to access it will be visible when you look at the array using the other variable.
To get two separate arrays you have to specifically create a copy of it:
var temp = a.slice(0);
Note: Copying an array makes a shallow copy. That means that if you for example have an array of objects that you copy, you will have two arrays, but they will still share the same set of objects.
As you know, primitive values are passed by value, everything else is passed by reference. Primitives are boolean, number and string (and maybe consider null and undefined here). Everything else is an object and passed by reference.
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
I don't understand why assigning the array to a new value doesn't affect the new array.
I know that "push" modifies the original array, splice also, and filter or slice doesn't, this is not my question. my question is that why assigning doesn't.
i have looked through old questions answers saying it is passed by reference but again if it is passed by reference then changing it's value should affect the reference also.
const modify = (someArray) => {
// modified my array
someArray.push(1)
// modified my original array
someArray[0] = 'A'
// didn't modify my array and I want to know why.
someArray = ['whatever']
}
let myArray = ['a', 'b']
modify(myArray)
console.log(myArray) // ["A", "b", 1]
In Javascript (and Java or C#) everything is by default passed by value!
The important is to know, what the value is. This line let myArray = ['a', 'b'] creates new array in memory and put the reference to it into myArray. Now, the value of myArray is the reference to the memory where ['a', 'b'] reside.
When you pass it to modify function, the value - which is the reference - is copied from myArray to someArray. It means someArray can access the same memory segment of the same array, therefore change the values inside that array, but you cannot change the value of myArray (which is the reference to that array) from someArray.
As I've described this to beginners many times, I've found that it's best to think of Objects in Javascript as being passed and assigned as a pointer (as in the way pointers work in C/C++). So, when you do this:
let a = [1,2,3];
let b = a;
You now have two variables that each have a pointer to the same [1,2,3] array. I find it works best to think about the array existing on its own and you now have two variables that each point at that array. When you assign b = a, it doesn't make a copy of the data, it just points b at the same data that a was pointing at.
If you modify that array with something like a.push(4) or assigning like a[0] = 9, then the one and only one array that both a and b point to has been modified. So, whether you access that array from a or from b, you will see the change because both variables point at the same physical array object.
But, if you reassign some other array to b like this:
b = [9,8,7];
You've just taken a new array and put a pointer to it in b. The other variable a still points to the same original array that is used to. It hasn't been changed in any way.
When you pass an array as an argument like you are doing in your modify() function, the function argument in the function is just like the b variable in the above example. It's just another variable that points at the same array. If you modify the array itself, then both variables will point at the same modified array. But, if you reassign the argument variable to now point at some other array, only that variable is affected.
['a', 'b'] is an array. myArray is a variable that refers to that array. someArray is a variable local to modify function that will take the value of myArray (i.e. the reference to your array; so both myArray and someArray refer to the same array). As you noted, someArray.push(1) and someArray[0] = 'A' both modify the array referred to by someArray. someArray = ["whatever"] will change the reference of someArray to the new array ['whatever'], but myArray still refers to the old array. At the end of the function, someArray variable disappears (and ['whatever'] is forgotten, since nothing refers to it any more).
There is no way in JavaScript to change the reference of the variable passed into a function (as e.g. C++ can); you can only manipulate what received reference points to.
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
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.
While reading a book about JavaScript I stumbled across an example:
var names = new Array("Paul","Catherine","Steve");
var ages = new Array(31,29,34);
var concatArray;
concatArray = names.concat(ages);
My question is, why doesn't the variable concatArray need to be define as a new Array() in order to store the concatenated data for both arrays name and ages , but when I try to treat the concatArray as an array by adding another line of code "document.write(concatArray[0])", it works just like an array and shows me the data stored in the first element. I just wonder why I'm not declaring the concatArray as a new array, yet it still works as one.
You are declaring concatArray as a new array but the declaration is implicit. The concat function returns a new array which contains concatenated copies of the original two arrays. The type of concatArray is inferred from the return type of the concat function.
Variable don’t have a specific data type in Javascript like in other languages. You can assign a variable every value you want.
That means var concatArray; declares the variable but the value is undefined:
var concatArray;
alert(typeof concatArray === "undefined");
Only when assigning the return value of names.concat(ages) (an array) to concatArray it get’s that type:
var names = new Array("Paul","Catherine","Steve");
var ages = new Array(31,29,34);
var concatArray;
alert(typeof concatArray === "undefined");
concatArray = names.concat(ages);
alert(concatArray.constructor === Array);
Javascript doesn't care what the contents of the var are when it is declared; that is why you can declare var concatArray without needing to specify it as an array. Once you assign it a value and a type (as the result of the concat() function) javascript treats the var as an array.
Simply put, w3schools says it pretty concisely:
The concat() method is used to join two or more arrays.
This method does not change the existing arrays, it only returns a copy of the joined arrays.
w3schools
Looks like Andrew and Matthew beat me to it anyway.
Because Javascript is dynamically typed. A variable doesn't have a specifuc type, and an array is an object that you can assign to any variable.
When you declare a variable without assigning it a value, it just exists with an undefined value:
var answer;
// now the variable exists, but it doesn't have a value
answer = 42;
// now the variable has the numerical value 42
answer = "hello";
// now the numerical value has been replaced with the string value "hello"
answer = [];
// now the variable contains an empty array
answer[0] = 1337;
// now the variable contains an array that contains an item with the value 1337
answer = -1
// now the array is gone and the variable contains the value -1
I would make an answer slightly different of Andrew's one.
JavaScript variables are not strongly typed. You can put a string, then a number, then an object in the same variable. When you use the variable, the interpreter checks its current type is suitable for the usage you try to make. If you write:
var a = 45;
alert(a[0]);
a = [ 5 ];
alert(a[0]);
you will get successively undefined then 5.