This question already has answers here:
Copy array by value
(39 answers)
Closed 3 years ago.
Class A is instantiated to an object that has an array named this.people that is filled with a bunch of data. Class A instantiates an object of class B and this.people is passed to it's constructor. If object B updates the array, when object A updates it, it overwrites the changes from object B. How can I fix this?
That happens because you're passing a reference of A's people array to B's constructor. You want to make either a "shallow copy" or a "deep copy" of A's people array, since you want B's people array contents to be different.
With a "shallow copy" you copy all of the primitive values in the array to the new one, but if you have compound values (arrays or objects) inside the array, then only the reference to them will be copied, so any changes that you do to the compound values will be reflected in both A and B. With a "deep copy", both primitive and compound values (and not just their references) will be copied to a new place in memory.
If you don't have objects and arrays inside the people array, you can do a shallow copy using Array.from(a.people). Otherwise, you can do a deep copy with JSON.parse(JSON.stringify(a.people))
Snippet:
class B {
constructor(_people) {
// shallow copy:
this.people = Array.from(_people);
// deep copy:
//this.people = JSON.parse(JSON.stringify(_people));
}
}
class A {
constructor() {
this.people = [];
}
func() {
this.objB = new B(this.people);
//do something
}
}
// Usage Example
let objA = new A();
objA.people.push("A");
objA.func();
objA.objB.people.push("B");
console.log(objA.people.toString());
console.log(objA.objB.people.toString());
Some helpful links:
https://medium.com/#gamshan001/javascript-deep-copy-for-array-and-object-97e3d4bc401a
https://dev.to/samanthaming/how-to-deep-clone-an-array-in-javascript-3cig
Give each class its own version of people
class A{
constructor(){
this.people = ["bob","jane","john"]
this.objB = new B(this.people)
}
}
class B{
constructor(people){
this.people = people
}
}
let objA = new A()
objA.objB.people.push("a")
objA.people.push("b")
console.log(objA.people, objA.objB.people)
Pass the array by value with destructuring.
class Family {
constructor(people) {
this.people = people;
}
}
let members = ["mom", "dad"];
let smiths = new Family(members);
let joneses = new Family(smiths.people); // pass by reference
joneses.people.push("Billy");
console.log(smiths.people); // changes to ["mom","dad","Billy"]
let wilsons = new Family([...smiths.people]); // pass by value
wilsons.people.push("Suzy");
console.log(smiths.people); // remains ["mom","dad","Billy"]
Related
This question already has answers here:
Is JavaScript a pass-by-reference or pass-by-value language?
(33 answers)
Closed 4 years ago.
I am struggling to understand the behavior of the following lines of code:
// I'd like to keep this value constant
let allObjects = [{value: null}, {value: 'Hello World'}]
// Here is a shorter list of objects matching some criteria
let someObjects = allObjects.filter(object => object.value)
// Here we work with the the values for some of the objects
someObjects[0].value = {hmm: 'test'}
// Kind of expecting allObjects[1].value to be 'Hello World' at this point
// Display all the objects
console.log(allObjects)
And the output:
[
{
"value": null
},
{
"value": {
"hmm": "test"
}
}
]
Here is a codepen.
What I do not understand is when the value of someObjects is modified it affects the value of allObjects and expect that allObjects[1].value will return Hello World.
Could someone explain to me why this is actually happening and how we are supposed create a sorter version of the array that does not mutate the original array when it is modified?
In JavaScript all primitive variables are passed by value however all objects are passed by a copy of its reference More Info. In your case, the array contains objects. Each index in the array contains a simple pointer to the memory location of an object. When you filter the array you are creating a new array with fewer values however the pointers are still pointing to the same internal objects. The filtered array, someObjects, contains a single pointer that points to the object { value: 'Hello World' }. You are overwriting the value property of this object with another object, { hmm: 'test' }. If you instead wanted to replace the object in the new filtered array rather than changing the value property of the old object you would do someObjects[0] = { hmm: 'test' }
const obj = { foo: 'bar' };
const copy = obj;
console.log(obj);
copy.foo = 'changed';
console.log(obj);
I don't understand why this behavior is happening. Lets say I define an object and make an array of 3 of this object. If I modify the objects in the array, it affects all instances of the object? Could someone explain why this is? Also, how do I make an array with independent "Copies" of the object to get the desired behavior? Thanks!
example
testObject = {"value1":"a","value2":"b"};
objArray = [];
for(i=0; i < 3; i++){
var newobj = testObject; //make a new testObject
objArray.push(newobj); //push new object to array
}
delete objArray[0].value2 // Desired, delete value 2 ONLY from array object 0
objArray[2].value2 //Undefined? Why is value2 missing from object 2
testObject.value2 //Undefined? Why is value2 missing from original object?
As opposed to primitives (strings, numbers, booleans, symbols null, undefined), objects in javascript are passed by reference. Variables serve as placeholders/pointers to these objects. To create a copy of an object without the risk of mutation you'd use spread (barring compatibility):
const newObject = { ...testObject };
or traditionally, Object.assign(), passing an empty object literal to avoid mutability of the original testObject:
const newObject = Object.assign({}, testObject);
As far as deep cloning, MDN suggests using a combination of JSON.parse() and JSON.stringify(). So for example:
const testObject = { value: "a", other: { value2: b } };
const newObject = JSON.parse(JSON.stringify(testObject));
You are pushing same object's reference again and again in the loop.
for(i=0; i < 3; i++){
var newobj = testObject; //no new object,same object's reference again
objArray.push(newobj); //push new object to array
}
It should be
for(i=0; i < 3; i++){
var newobj = {"value1":"a","value2":"b"}; //make a new testObject
objArray.push(newobj); //push new object to array
}
When creating an Object in JavaScript, you are actually creating a reference to that object. You can store this in a variable and pass it around ... perhaps append it to an array. When you go to do operations on an object reference, it finds the original object that the reference points to and updates it. Thus when you use .push it's not creating a new object but simply pushing the reference to that object. If you update it in one spot it will update it in the other and any others where you have assigned that reference.
Copying an object into a new object is generally called cloning. There are a lot of different ways to clone objects in JavaScript with varying results.
You can use var newobj = { ...testObject } as the other answer suggests. This spread operator essentially copies the properties of testObject and creates a new object (declared with the outer { }). The reference to that new object is then assigned to newobj. You can think of it as doing this:
var newobj = {
value1: testObject.value1,
value2: testObject.value2,
};
However, you should keep in mind that this gives you only one level of cloning. That is to say if your object contains other objects then the reference to that object will be assigned as the property rather than a clone of that object. For example: let's say you had:
var testObject = { obj: { a: "b" } };
var newobj = { ...testObject };
delete testObject.obj.a;
console.log(newobj); // { obj: {} }
In order to solve this, you need to do what is called a deep clone which in JavaScript can be done by recursively cloning object properties that are also objects. There are a bunch of ways to do this including libraries like lodash or home-grown functions. One example on SO: What is the most efficient way to deep clone an object in JavaScript?
Finally, if testObject is supposed to be something like an object template or initial state from which other newobj are derived, it might make more sense to use a function:
function newObjFactory() {
return {
value1: "a",
value2: "b",
};
}
Then you can do var newobj = newObjFactory() and you'll get a new object each time since a new object is created by the function each time it's called and returned.
Is there a way to store an object with an array[id] = "certain value", so that every single user has a list of favorite things ?
What I'm trying to achieve is having a data object with undefined values at first, and filling them in, so for example Jack and Sam could have an assigned favorite movie saved.
I tried something like this with no success:
Data.js:
module.exports = function() {
return {
favMovie: ""
}
};
App.js:
var person [] = data();
//1st person
person[811767132257839].favMovie = "Matrix";
//2nd person
person[107230716367889].favMovie = "Kill Bill";
//3rd person
person[973676332752239].favMovie = "Avatar";
...
console.log( "Favorite movie: " + person[id].favMovie );
It doesn't sound like you want any arrays at all, just objects.
// Create an object that we'll keep person objects in
var personData = {};
// Add a person object to it for person ID #123
personData[123] = {};
// Set person #123's favorite movie:
personData[123].favMovie = "Avatar";
// Add a different person; this time, we'll add the movie at
// the same time
personData[234] = {
favMovie: "Matrix"
};
When using objects as maps like that, sometimes people create the objects using Object.create(null) to avoid them having any inherited properties (like toString and valueOf and constructor):
person[123] = Object.create(null);
person[123].favMovie = "Avatar";
In ES2015 (aka "ES6"), you might want to use a Map rather than an object for the map of people:
var personData = new Map();
...and then use set and get for the individual person objects.
If the individual person objects get complicated, you might use a constructor function and associated prototype for them, using either ES5 syntax:
function Person() {
// ...
}
Person.prototype.doSomething = function() {
// ...
};
...or ES2015 syntax:
class Person {
constructor() {
// ...
}
doSomething() {
// ...
}
}
Then creating them:
personData[123] = new Person();
// or if using Map
personData.set(123, new Person());
Side note: Even when we write them as numbers, the keys (property names) in objects are always strings (unless you use ES2015 Symbols, which you probably wouldn't here). In contrast, keys in an ES2015 Map can be anything. Key equality in Map instances is determined using the special "same value zero" algorithm (which is basically === except that NaN is equal to itself [whereas it isn't in ===]).
I want to have an object that inherits an array property and a method to add elements to the inherited array. However, the inherited method yChange() changes the prototype array and not the inherited array. This question explains why the undesired behavior happens. But can't figure out how to get the desired behavior.
var parent = {
x: 0,
y: [],
xChange: function () {this.x += 1},
yChange: function () {this.y.push(1)}
};
var child = Object.create(parent);
child.xChange();
child.yChange();
console.log(child.x, child.y); // 1 [1]
console.log(parent.x, parent.y); // 0 [1]
Desired:
console.log(child.x, child.y); // 1 [1]
console.log(parent.x, parent.y); // 0 []
However, the inherited method yChange() changes the prototype array and not the inherited array.
There is no difference between the "inherited" array and the "prototype" array. They are one and the same.
You have to give child its own array:
var child = Object.create(parent);
child.y = [];
So, I can't inherit an 'own' array as with the number? The question is how to do it with an inherited array.
Everything that is inherited is not "owned" by the child. Even numbers. The difference is that numbers are not mutable, hence the issue is not apparent.
Look closely what here:
this.x += 1
You are assigning a new value to this.x. This will create child.x, not modify parent.x.
Lets look at
this.y.push(1);
You are not assigning anything here. You are reading this.y, which resolves to parent.y and you are mutating the array object itself.
Is it clearer now why you have to assign a new array to child.y (child.y = [];) ? The assignment is what gives the child its own copy of the data.
The difference between the number and array case is that numbers are immutable and arrays are mutable. The immutability of numbers forces you to create a new number and assign it.
Not so with mutable values. You have to explicitly create a copy of the value if you don't want it to be shared (and that's what child.y = []; is basically doing).
Felix is right about an assignment being necessary to change child.y. In your example, you could check to see if the memory addresses are the same first and then assign a new one for the new instance if they match. Like so:
var parent = {
x: 0,
y: [],
xChange: function () {this.x += 1},
yChange: function () {
if (this.y == Object.getPrototypeOf(this).y)
this.y = new Array()
this.y.push(1)
}
};
var child = Object.create(parent);
child.xChange();
child.yChange();
console.log(child.x, child.y); // 1 [1]
console.log(parent.x, parent.y); // []
This question already has answers here:
Javascript object members that are prototyped as arrays become shared by all class instances
(3 answers)
Closed 8 years ago.
Given the following code:
var Car = function() {};
Car.prototype = {
wheels: {
rims: 'steel'
}
}
var volvo = new Car;
var mercedes = new Car;
volvo.wheels.rims = 'aluminium';
console.log(volvo.wheels.rims, mercedes.wheels.rims); // 'aluminium', 'aluminium'
Can you explain why instance mercedes of Auto automatically inherits the sub-property definition for rims from volvo?
Note the following code works as expected with same setup:
volvo.wheels = 4;
console.log(volvo.wheels, mercedes.wheels); // 4, Object { rims: 'steel' }
You only ever create a single object for wheels.
You assign this object to the prototype, so every instance inherits its value.
Javascript will never automatically copy an object.
Instead, you should create the object in the constructor so that you get a new object for each instance.