I have a loop goes through an array of objects MyArrayOfObjects and then pushes the objects to a new array like this:
var NewArray = new Array();
for (i = 0; i < MyArrayOfObjects.length; i++) {
TempObject = null;
TempObject = new Object();
// I have logic that copies certain properties but not others
// but overall it looks like this:
TempObject.prop1 = MyArrayOfObjects[i].prop1;
TempObject.prop2 = MyArrayOfObjects[i].prop2;
NewArray.push(TempObject);
}
As I loop through MyArrayOfObjects, I clear the TempObject and create a new one each time. Does NewArray contain the objects that I'm copying or just a reference to the objects copied and that then become deleted as the loop iterates?
Thanks.
It contains references to the objects themselves.
This code shows that concept in action (notice that changing the object after pushing it into the array changes the object in the array as well):
var ray = new Array();
var obj = { foo: 123 };
ray.push(obj);
obj.foo = 321;
alert(ray[0].foo);
> var NewArray = new Array();
It is generally considered better to use an array literal to create an array. Variable names starting with a capital letter are, but convention, used for constructors. Using "new" at the start of a variable name can easily slip to become "new Array", and the name should reflect its purpose, so something like the following might be better:
var objectArray = [];
.
> for (i = 0; i < MyArrayOfObjects.length; i++) {
You should always declare variables, especially counters as undeclared variables are made properties of the global object (effectively global variables) when they are first assigned a value. Also, it is considered better to store the length of the array than get it in each iteration:
for (var i = 0, iLen = MyArrayOfObjects.length; i < iLen; i++) {
.
> TempObject = null;
> TempObject = new Object();
Again, declare variables. Assigning a value of null serves no useful purpose when you're going to assign some other value immediately afterward. Just do the second assignment (and use a literal):
var TempObject = {};
.
> // I have logic that copies certain properties but not others
> // but overall it looks like this:
>
> TempObject.prop1 = MyArrayOfObjects[i].prop1;
> TempObject.prop2 = MyArrayOfObjects[i].prop2;
>
> NewArray.push(TempObject);
At this point, TempObject and NewArray[NewArray.length - 1] both reference the same object.
> }
As I loop through MyArrayOfObjects, I clear the TempObject and create
a new one each time.
There is no need to "clear" the object, just assign a new value to the variable. In javascript, all variables have a value that might be a primitive (e.g. string, number) or a reference to an object (e.g. Object, Array, Number, String)
Does NewArray contain the objects that I'm
copying or just a reference to the objects copied and that then become
deleted as the loop iterates?
It contains references to the new objects created on each iteration.
As variables hold references to objects, assigning a new value to the variable doesn't do anything to the object. When an object is no longer referenced by any variable or object property, it is made available for garbage collection and may be removed automatically at some later time when garbage collection runs.
Using map or its jquery counterpart might be a more idiomatic way of doing this. For example:
var oldArray = [
{ prop1: 1, prop2: 10 },
{ prop1: 2, prop2: 20 },
{ prop1: 3, prop2: 30 }
]
var newArray = $.map(oldArray, function(oldObj) {
return { newProp: oldObj.prop1 }
})
console.log(newArray)
Related
Here is an example that you can type in your console. Super new to Javascript. The example is reproducible by opening a new tab and typing it out in a console (The JSX Fiddle's console feature is in beta, so I'm not sure if it can be trusted)
let clothing = ['card0', 'card1', 'card2', 'card3'];
let timers = {}
let timerObj = {"startTime": null, "pauseTime": null, "elapsedTime": null, "hasSubmitted": false} //Nested object I want for all indices, will manipulate 0th index alone inside for loop
for (var i = 0; i < clothing.length; i++) {
timers[i] = timerObj
if (i == 0) {
timers[i]["startTime"] = Date.now();
}
}
console.log(timers)
What I'm intending to do is, for the 0th index alone, set the timers[0]["startTime"] as Date.now(), and for the rest, let the startTime be null as defined in the timerObj.
Strangely, after running the for loop, I see that for all i, the Date.now() has been set. I understand that Javascript objects are mutable, but why is why are all indices being set to Date.now()?
I looked at other Javascript related Object questions related to a concept call "freezing", not sure I have my basics right.
EDIT: I think this is related the object reference being altered..
var clothing = ['card0', 'card1', 'card2', 'card3'];
var timers = {}
var timerObj = {"startTime": null, "pauseTime": null, "elapsedTime": null, "hasSubmitted": false} //Nested object I want for all indices, will manipulate 0th index alone inside for loop
for (var i = 0; i < clothing.length; i++) {
timers[i] = Object.assign({}, timerObj)
if (i == 0) {
timers[i]["startTime"] = Date.now();
}
}
console.log(timers)
You can refer this for more information on this topic.
You have to clone your object. There are multiple ways to clone. One would be spread operator(...). Like below:
let clothing = ['card0', 'card1', 'card2', 'card3'];
let timers = {}
let timerObj = {"startTime": null, "pauseTime": null, "elapsedTime": null, "hasSubmitted": false}
clothing.forEach((val, i)=>{
timers[i] = {...timerObj};
if(i==0){
timers[i].startTime = Date.now()
}
});
console.log(timers);
Javascript does not copy objects. It passes references around, so when you assign timers[i] = timerObj once, then you assign Date.now() once , this value goes to your single timeorObj. All subsequent assignments of timerObj to timers[i] for all i refer to the single timerObj you defined.
To fix this force a copy: timers[i] = JSON.parse(JSON.stringify(timerObj));
This will serialize your clean timerObj to a JSON string, then convert it back to a new javascript object and then assign the new object to timers[i].
This way, you end up with copies of timerObj in each slot of your timers array.
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.
I want to create an Object that contains one or more two dimensional arrays in Javascript.
I tried it the following way (in this example I only try to add one two dimensional array):
var XSIZE = 8;
var YSIZE = 8;
var obj = {
field : new Array(XSIZE),
field[0] : new Array(YSIZE),
foo : 1,
bar : 100
}
Info:
- This gives me a strange error "missing : after property id" which does not seem to make much sense
- Unfortunately I didn't find examples showing how to do this so far by using google
- If I don't add field[0] ... for creating the 2nd array it works.
- changing the XSIZE and YSIZE to numbers like new Array(8)... doesn't work.
I would really appreciate if somebody could show me how to do it or explain why I cannot do this at all and need to use some other method.
Thanks a lot!
The error "missing : after property id" is because JavaScript sees the field part of field[0] and expects a colon before the value of that field. Instead it gets an open bracket so it complains.
You can't hard code an object definition that has its dimensions set up at run time. You have to build the object at run time as well. Like this perhaps
var XSIZE = 8;
var YSIZE = 8;
var obj = {
field : new Array(),
foo : 1,
bar : 100
}
for (var i = 0; i < XSIZE; i++) {
obj.field.push(new Array(YSIZE));
}
In object literal notation, the property names must be exactly that: property names. Firstly, field[0] isn't a property name. Secondly, the properties don't exist until the after the object defined, so you can't access properties until then.
What you should do is either set the array after the object is created:
var obj = {...}
obj.field[0] = [...];
or nest the array literals:
var obj = {
field: [ [...],
...
],
...
}
You don't need to worry about setting the array size when creating the array, as it will grow when you add elements.
You can only declare properties on the object being constructed that way; not on objects in another "level".
You could use a for loop instead:
for(var i = 0; i < XSIZE; i++) {
obj.field[i] = new Array(YSIZE);
}
Note that the YSIZE is not necessary since an empty array works just fine as well ([]).
You could get the two dimensional array as your obj property, without resorting to external procedures and keep everything internal to the object. Create your empty 'field' array 1st.
var obj = {
field:[],
foo:1,
bar:100
};
Now, create an object's method to create a two dimensional array off your initial dimensionless array. You can determine the length and the number of dimensions of multi dimension array as you wish at run time:
var obj = {
field:[],
multifield:function(x,y){for (var count=0;count<x;count++) {this.field[count]=new Array(y);}},
foo:1,
bar:100
};
You can then call the obj.multifield method entering whatever dimensions you decide:
obj.multifield(10,5); //-->create a 10x5 array in this case...
console.log(obj.field.length); // 10
console.log(obj.field[0].length); // 5
I feel puzzled when I rethink of these two functions:
The first one goes like this:
var test = [1,2,3];
var ele = test[0];
ele= 2;
alert(test[0]);
The result is 1. I think this is obvious. But when I meet this:
var test = [{id:1},{},{}];
var ele = test[0];
ele.id = 2;
alert(test[0].id);
The result turns to be 2
So could anyone tell me that how the javascript work when it happens like this in the object array?
In JavaScript, objects are assigned by reference, rather than copied in memory. So if you assign an existing object to a different variable, both will point to the same object in memory. Modifications to either will therefore be reflected in both.
var a = {id: 1, name: "bob"};
var b = a;
console.log(b.name); // bob
b.name = "bill";
console.log(a.name); // bill
So in your example, executing ele.id = 2; operates on the memory location holding the object at test[0]. The change to the id property of that object is reflected in both variables referencing it (test[0], ele)
Note that if you had assigned the entire array test to ele, modifying one of the array members would have been reflected in both test, ele since Arrays are objects in Javascript:
var test = [1,2,3];
// Assign array test to ele
var ele = test;
// Modify one member
ele[0] = 2;
alert(test[0]); // 2
I'm faced with a situation in JavaScript when I need to update an object via its pointer similar to ะก++ array of pointers to objects
Example code for my issue:
var foo = new Array();
var bar = function(){
this.test = 1;
foo.push(this); // push an object (or a copy of object?) but not pointer
};
var barInst = new bar(); // create new instance
// foo[0].test equals 1
barInst.test = 2;
// now barInst.test equals 2 but
// foo[0].test still equals 1 but 2 is needed
So, how can I solve this? Should I use a callback or something like this or there is an easy way to help me to avoid copying the object instead pushing the raw pointer into an array?
JS is pass-by-value, so your original assignment was this.test = the value of 1, in my example, it's this.test = the object pointed to by ptr, so when I change ptr this.test changes as well.
var foo = [],
ptr = {val: 1},
bar = function(){
this.test = ptr;
foo.push(this); // push an object (or a copy of object?) but not pointer
},
barInst = new bar(); // create new instance
// foo[0].test.val equals 1
ptr.val = 2;
// foo[0].test.val equals 2
Although if you thought that foo.push(this); was similar, it isn't. Since this is an object, the array will indeed contain "raw pointers" to objects, just like you want. You can prove this simply:
foo[0].test = 3;
// barInst.test === 3
Which shows that it is indeed a pointer to the object that was pushed onto the array
"create object method pointer"
Object.defineProperty(Object.prototype,'pointer',{
value:function(arr, val){
return eval(
"this['"+arr.join("']['")+"']"+
((val!==undefined)?("="+JSON.stringify(val)):"")
);
}
});
ex of use
var o={a:1,b:{b1:2,b2:3},c:[1,2,3]}, arr=['b','b2']
o.pointer(arr) // value 3
o.pointer(['c',0], "new_value" )