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
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 know that ES6 is not standardized yet, but a lot of browsers currently support const keyword in JS.
In spec, it is written that:
The value of a constant cannot change through re-assignment, and a
constant cannot be re-declared. Because of this, although it is
possible to declare a constant without initializing it, it would be
useless to do so.
and when I do something like this:
const xxx = 6;
xxx = 999;
xxx++;
const yyy = [];
yyy = 'string';
yyy = [15, 'a'];
I see that everything is ok: xxx is still 6 and yyy is [].
But if I do yyy.push(6); yyy.push(1); , my constant array has been changed. Right now it is [6, 1] and by the way I still can not change it with yyy = 1;.
Is this a bug, or am I missing something? I tried it in the latest chrome and FF29
The documentation states:
...constant cannot change through re-assignment
...constant cannot be re-declared
When you're adding to an array or object you're not re-assigning or re-declaring the constant, it's already declared and assigned, you're just adding to the "list" that the constant points to.
So this works fine:
const x = {};
x.foo = 'bar';
console.log(x); // {foo : 'bar'}
x.foo = 'bar2';
console.log(x); // {foo : 'bar2'}
and this:
const y = [];
y.push('foo');
console.log(y); // ['foo']
y.unshift("foo2");
console.log(y); // ['foo2', 'foo']
y.pop();
console.log(y); // ['foo2']
but neither of these:
const x = {};
x = {foo: 'bar'}; // error - re-assigning
const y = ['foo'];
const y = ['bar']; // error - re-declaring
const foo = 'bar';
foo = 'bar2'; // error - can not re-assign
var foo = 'bar3'; // error - already declared
function foo() {}; // error - already declared
This happens because your constant is actually storing a reference to the array. When you join something into your array you are not modifying your constant value, but the array it points to. The same would happen if you assigned an object to a constant and tried to modify any property of it.
If you want to freeze an array or object so it can't be modified, you can use the Object.freeze method, which is already part of ECMAScript 5.
const x = Object.freeze(['a'])
x.push('b')
console.log(x) // ["a"]
Came through this article while searching on why I was able to update an Object even after defining it as const. So the point here is that it is not the Object directly but the attributes it contains which can be updated.
For example, my Object looks like:
const number = {
id:5,
name:'Bob'
};
The above answers correctly pointed out that it's the Object which is const and not its attribute. Hence, I will be able to update the id or name by doing:
number.name = 'John';
But, I will not be able to update the Object itself like:
number = {
id:5,
name:'John'
};
TypeError: Assignment to constant variable.
This is consistent behavior with every programming language I can think of.
Consider C - arrays are just glorified pointers. A constant array only means that the value of the pointer will not change - but in fact the data contained at that address is free to.
In javascript, you are allowed to call methods of constant objects (of course - otherwise constant objects would not serve much purpose!) These methods might have the side effect of modifying the object. Since arrays in javascript are objects, this behavior applies to them as well.
All you are assured of is that the constant will always point to the same object. The properties of the object itself are free to change.
The keyword const is a little misleading.
It does not define a constant value. It defines a constant reference to a value.
Because of this you can NOT:
Reassign a constant value
Reassign a constant array
Reassign a constant object
But you CAN:
Change a constant array
Change a constant object
I think this would give you more clarity on the issue : https://codeburst.io/explaining-value-vs-reference-in-javascript-647a975e12a0 .
Basically it boils down to the const always pointing to the same address in memory. You can change the value stored in that address but cannot change the address the const is pointing too.
The definition of const you mentioned will hold true when the const is pointing to an address that holds a primitive value . This is because you cannot assign a value to this const without changing its address (because this is how assigning primitive values work) and changing the address of a const is not allowed.
Where as if the const is pointing to non-primitive value , it is possible to edit the value of the address.
The const declaration creates a read-only reference to a value. It does not mean the value it holds is immutable, just that the variable identifier cannot be reassigned. For instance, in the case where the content is an object, this means the object's contents (e.g., its parameters) can be altered.
In addition, an also important note:
Global constants do not become properties of the window object ...
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const
The value of a const can't be changed through reassignment, and it can't be redeclared.
const testData = { name:"Sandeep",lastName:"Mukherjee",company:"XYZ"}
First case
testData = {name:"hello"}
console.log(testData);//throws an Error:Assignment to constant variable
Here we are reassigning testData again
Second case
const testData = {name:"Sandeep",lastName:"Mukherjee",company:"ABC"}
console.log(testData); //throws an Error: Identifier 'testData' has already been declared
Here we are redeclaring testData again
When a variable is declared using const it means it points to some memory location the
behaviour of const is we can manipulate the value stored in that memory location but not
the memory location,when we reassign/redeclare the const variable it
does not allow to change the memory location
We can change the value of a specific key
testData.company = "Google"
console.log(testData);
//{ name: 'Sandeep', lastName: 'Mukherjee', company: 'Google' }
We can add any new key value pair to it
testData.homeTown = "NewYork"
console.log(testData)
//{name: 'Sandeep',lastName:'Mukherjee',company:'Google',homeTown: 'NewYork'}
Because in const you can change the values of an object, so the object does not actually store the assignment data but instead, it points to it.
so there is a difference between primitives and objects in Javascript.
const variable stores the address (memory address such as 0xFF2DFC) that is constant.
The constant is NOT the content at the memory.
constant is memory address ONLY
Thank you for reading.
const MY_OBJECT = {'key': 'value'};
// Attempting to overwrite the object throws an error
// Uncaught TypeError: Assignment to constant variable.
MY_OBJECT = {'OTHER_KEY': 'value'};
// However, object keys are not protected,
// so the following statement is executed without problem
MY_OBJECT.key = 'otherValue';
// Use Object.freeze() to make object immutable
// The same applies to arrays
const MY_ARRAY = [];
// It's possible to push items into the array
MY_ARRAY.push('A'); // ["A"]
// However, assigning a new array to the variable throws an error
// Uncaught TypeError: Assignment to constant variable.
MY_ARRAY = ['B'];
In your constant is saved not the object, but link to the object.
You can't change this link, because it is constant.
But object you can change.
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 am working with Node for the first time and I have two modules. One module defines an array of objects, and also has functions that are exported for use in the other module - including lookupbyID, lookupbyLastName, and addEmployee.
My issue is that when I call the functions from module 1 in module 2, which return objects from the original array and assign those objects to a variable, and then I modify that variable, it modifies the original data. Please see the following code:
Module 1:
const us = require('underscore')
var data = [
{id:1, firstName:'John', lastName:'Smith'},
{id:2, firstName:'Jane', lastName:'Smith'},
{id:3, firstName:'John', lastName:'Doe'},
]
exports.lookupByID = function (given_id) {
var found_id = us.findWhere(data, {id:given_id});
return found_id;
}
Module 2:
const employeeFunctions = require("./employeeModule");
var id_2_answer = employeeFunctions.lookupByID(2);
id_2_answer.firstName = 'Mary'
console.log(employeeFunctions.lookupByID(2))
As you can see, I changed the name of Jane to Mary. Even though I assigned the object to a variable, changing the variable changed the original object data, which I verified by printing the lookupbyID function a second time.
Can you help me understand why this happens? Can you help me understand possible ways to prevent this from happening? I would like to be able to assign the object to a variable, and be able to change the values within the variable without affecting the original data.
Thank you!
In js objects are assigned by reference so:
var foo = {bar:1};
var baz = foo;
baz.bar; // 1
baz.bar = 2;
foo.bar; // 2
Use Object.assign to clone objects instead:
var foo = {bar:1};
var baz = Object.assign({}, foo);
baz.bar; // 1
baz.bar = 2;
foo.bar; // 1
The short answer is that you can't - you've hit on a key aspect of programming: value vs reference. You can read more about it here but the long and short of it is that both of these variables point to the same object in memory, so changing it for one changes it for the other.
The solution will necessarily involve creating a new object - the neatest way is probably to use Object.assign to copy over values, but bear in mind that in your example, you'll probably need to delete your old entry too.
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.