This question already has answers here:
Javascript object members that are prototyped as arrays become shared by all class instances
(3 answers)
Closed 6 years ago.
I think that I did not understand the whole prototype flow, I have this problem:
function SomeO() {};
SomeO.prototype.arr = [];
SomeO.prototype.str = "";
var s1 = new SomeO();
var s2 = new SomeO();
s1.str+="1"
console.log(s2) // "" OK
s1.arr.push(1)
console.log(s2) // [1] WHY???
Why when I add an item to an array in one object it has the same array instance?
That's because objects are shared by reference by all the instances of your "SomeO" object, in this case your "arr" attribute, and things like strings or numbers are shared by value, so the modification of the string will not affect the values of other instances.
so in that case is normal to get that result.
function SomeO() {};
SomeO.prototype.arr = [];
SomeO.prototype.str = "chimichangas";
var s1 = new SomeO();
var s2 = new SomeO();
s1.str+="1"
console.log(s1.str); // "chimichangas1" OK because is by value
console.log(s2.str); // "chimichangas" OK because is by value
s1.arr.push(1);
console.log(s2.arr); // [1] WHY??? because is by reference
And if you don't want to share the array you should do something like.
function SomeO() {
this.arr = [];
};
SomeO.prototype.str = "";
var s1 = new SomeO();
var s2 = new SomeO();
s1.str+="1"
console.log(s1.str); // "1" OK
console.log(s2.str); // "" OK
s1.arr.push(1);
console.log(s1.arr); // [1] Ok
console.log(s2.arr); // [] Ok
Because both instances share the same [[Prototype]](the Object assigned to SomeO.prototype), the also share the same Array in SomeO.prototype.arr.
You can check it yourself:
s1.arr === s2.arr
To circumvent this, you could define the array (and all other objects you might need) in the constructor instead:
function SomeO() {
this.arr = [];
this.obj = {}; // define all arrays and objects in the constructor if they need to be separate
}
Because that's how you defined the array: you create one array object on the prototype which is shared between all instances. Typically you'll only want to put functions and constant values on the prototype. Every instance property needs to be created inside the constructor:
function SomeO() {
this.arr = [];
}
When you reference a property of an object (e.g. s1.arr), it first checks if the property exists on the object, if it does, it returns it, if it doesn't, it falls back to the object's prototype.
When you do s1.str += "1", which is equivalent to s1.str = s1.str + "1", you're setting the str property on the object itself, the prototype's str property doesn't change. s1.str will return the new string from s1, and s2.str will fall back to prototype.str.
s1's str s2's str prototype's str s1.str s2.str
before: - - "" "" (from proto) "" (from proto)
after: "1" "" "1" (from own) "" (from proto)
When you do s1.arr.push(1) you get s1.arr from the prototype, and you change its value. You never set the arr property on s1.
s1's arr s2's arr prototype's arr s1.arr s2.arr
before: - - [] [] (from proto) [] (from proto)
after: - - [1] [1] (from proto) [1] (from proto)
Related
This question already has answers here:
Crockford's Prototypal inheritance - Issues with nested objects
(3 answers)
Closed 7 years ago.
I am trying to understand how Object.create copies arrays and objects properties when initiating a new object. It seems to be different then copying a string or number. For example if we have a basic Object with a number and array property. jsfiddle example
var obj = {
num: 0, arr: []
};
We then initiate 3 new Objects from this base.
var set1 = Object.create(obj);
set1.num = 10;
set1.arr.push(1);
var set2 = Object.create(obj);
var set3 = Object.create(obj, {arr: []});
I was expecting set2.num and set2.arr property to be it's initial state. I found this to be true for the number, but not the array. Of course one work around is to pass {arr: []} when initiating the Object or creating a initiation function that resets the arr property.
// false
console.log(set1.num === set2.num);
// true - why is this true???
console.log(set1.arr === set2.arr);
// false
console.log(set1.arr === set3.arr);
Is this the normal behavior? Is Object.create keeping a reference to all of the Object's array and object properties? It would be very nice to not have to create new arrays and objects when initiating a new Object.
It would be very nice to not have to create new arrays and objects when initiating a new Object
Write a function in your favourite style
Returning a literal
function makeMyObject() {
return {num: 0, arr: []};
}
// usage
var obj = MyObject();
Returning an Object.created object, and assigning to it,
function makeMyObject() {
var o = Object.create(null); // or some prototype instead of `null`
return Object.assign(o, {num: 0, arr: []});
}
// usage
var obj = MyObject();
Using new
function MyObject() {
this.num = 0;
this.arr = [];
}
// usage
var obj = new MyObject();
Cloning is a bit more complicated, a basic example might be
function shallowClone(o) {
var e;
if (typeof o !== 'object')
return o;
e = Object.create(Object.getPrototypeOf(o));
// copy enumerable references
Object.assign(e, o);
// or to keep non-enumerable properties
// Object.defineProperties(b, Object.getOwnPropertyNames(o).map(Object.getOwnPropertyDescriptor.bind(Object, o)));
return e;
}
Deep cloning requires looping over properties (e.g. for..in for enumerable only) and type checking instead of simply copying everything over. You usually end up needing to recurse on properties which are Objects themselves.
For known types, you can teach it to use the correct constructor too, e.g.
if (Array.isArray(o)) {
e = [];
o.forEach((v, i) => e[i] = recurse(v));
}
Where recurse would be the name of the clone function
I am very new to JS, have been working in C/C++ before,
I need an equivalent of below C structure in JSON
struct tmp_t{
int a;
char c_str[1024];
};
struct tmp2_t{
int a2;
.
.
char c2_str[1024];
};
struct my {
int number;
struct tmp_t tmp[100];
struct tmp2_t tmp2[100][1000];
};
For a json like
var myJSON = {
"number":0,
.
.
};
I need to access it like
myJSON.tmp[0].a = 10;
myJSON.tmp2[0][1].c2_str = "hello world"
any input is highly appreciated
Javascript properties are not typed like they are in C so there is no purely "equivalent" expression in javascript. You don't predeclare typed data structures like your C code has. I given variable or property in javascript can be assigned any value or reference - there is not hard typing. So without variables that can only contain a specific type like C has, there's no pre-declaring of data structure definitions like you have included from C.
Instead, you just declare the properties you want to use on a live object or if you intend to use many of them, you can create a prototype which you can instantiate when needed.
A direct declaration of a live object instance somewhat like your last structure would look like this:
var my = {
number: 10,
tmp: new Array(100),
tmp2: new Array(100)
};
This would declare an object named my that had three properties called number, tmp and tmp2. number initially contained the number 10 and the other two properties contained arrays of length 100 who's values were undefined. I don't know of any compact way to predefine your two dimensional array in javascript without running code in a loop to initialize it.
This data defintion would let you access my.number, my.tmp and so on.
If you want your arrays to contains objects with properties themselves, then you need to populate those arrays with the objects.
var my = {
number: 10,
tmp: [{a: 1, c_str: "foo"}, {a: 2, c_str: "whatever"}],
tmp2: new Array(100)
};
Or, in code, you could add in item to the tmp array with code like this:
var my = {
number: 10,
tmp: [],
tmp2: []
};
my.tmp.push({a: 1, c_str: "foo"});
my.tmp.push({a: 2, c_str: "whatever"});
Or, you could create the object separately and then put it in the array:
var obj = {}; // new empty object
obj.a = 1; // assign property
obj.c_str = "foo"; // assign property
my.tmp.push(obj); // put object into the array
obj = {}; // new empty bject
obj.a = 2;
obj.c_str = "whatever";
my.tmp.push(obj);
Or, you could assign each property individually like this:
my.tmp.push({}); // put empty object into the array
my.tmp[0].a = 1; // assign property to the object
my.tmp[0].c_str = "foo"; // assign property to the object
my.tmp.push({});
my.tmp[1].a = 2;
my.tmp[1].c_str = "whatever";
In either case, you could then access the data like this:
console.log(my.tmp[0].a); // 1
console.log(my.tmp[0].c_str); // "foo"
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)
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" )
while review a javascript coding, i saw that
var detailInf = {
"hTitle":"Results",
"hMark":"98"
};
What's the concept behind this js coding. While give alert for the variable its shows as "[object Object]". So this is an object, then how can we access the variable and reveal the data from this object.
Try doing this:
alert(detailInf['hTitle']);
alert(detailInf.hTitle);
Both will alert "Results" - this is a Javascript object that can be used as a dictionary of sorts.
Required reading: Objects as associative arrays
As a footnote, you should really get Firebug when messing around with Javascript. You could then just console.log(detailInf); and you would get a nicely mapped out display of the object in the console.
That form of a JavaScript object is called an object literal, just like there are array literals. For example, the following two array declarations are identical:
var a = [1, 2, 3]; // array literal
var b = new Array(1, 2, 3); // using the Array constructor
Just as above, an object may be declared in multiple ways. One of them is object literal in which you declare the properties along with the object:
var o = {property: "value"}; // object literal
Is equivalent to:
var o = new Object; // using the Object constructor
o.property = "value";
Objects may also be created from constructor functions. Like so:
var Foo = function() {
this.property = "value";
};
var o = new Foo;
Adding methods
As I said in a comment a few moments ago, this form of declaring a JavaScript object is not a JSON format. JSON is a data format and does not allow functions as values. That means the following is a valid JavaScript object literal, but not a valid JSON format:
var user = {
age : 16,
// this is a method
isAdult : function() {
// the object is referenced by the special variable: this
return this.age >= 18;
}
};
Also, the name of the properties need not be enclosed inside quotes. This is however required in JSON. In JavaScript we enclose them in brackets where the property name is a reserved word, like class, while and others. So the following are also equivalent:
var o = {
property : "value",
};
var o = {
"property" : "value",
};
Further more, the keys may also be numbers:
var a = {
0 : "foo",
1 : "bar",
2 : "abz"
};
alert(a[1]); // bar
Array-like objects
Now, if the above object would have also a length property, it will be an array like object:
var arrayLike = {
0 : "foo",
1 : "bar",
2 : "baz",
length : 3
};
Array-like means it can be easily iterated with normal iteration constructs (for, while). However, you cannot apply array methods on it. Like array.slice(). But this is another topic.
Square Bracket Notation
As Paolo Bergantino already said, you may access an object's properties using both the dot notation, as well as the square bracket notation. For example:
var o = {
property : "value"
};
o.property;
o["property"];
When would you want to use one over the other? People use square bracket notation when the property names is dynamically determined, like so:
var getProperty = function(object, property) {
return object[property];
};
Or when the property name is a JavaScript reserved word, for example while.
object["while"];
object.while; // error
That's an object in JSON format. That's a javascript object literal. Basically, the bits to the left of the :'s are the property names, and the bits to the right are the property values. So, what you have there is a variable called detailInf, that has two properties, hTitle and hMark. hTitle's value is Results, hMark's value is 98.
var detailInf = { "hTitle":"Results", "hMark":"98"};
alert(detailInf.hTitle); //should alert "Results"
alert(detailInf.hMark); //should alert "98
Edit Paolo's answer is better :-)
As Dan F says, that is an object in JSON format. To loop through all the properties of an object you can do:
for (var i in foo) {
alert('foo[' + i + ']: ' + foo[i]);
}