Javascript object members that are prototyped as arrays become shared by all class instances
The prototype of an object is just an object. The prototype properties are shared between all objects that inherit from that object. No copies of the properties are made if you create a new instance of a "class" (classes don't exist anyway in JS), i.e. an object which inherits from the prototype.
It only makes a difference on how you use the these inherited properties:
function Foo() {}
Foo.prototype = {
array: [],
func: function() {}
}
a = new Foo();
b = new Foo();
a.array.push('bar');
console.log(b.array); // prints ["bar"]
b.func.bar = 'baz';
console.log(a.func.bar); // prints baz
In all these cases you are always working with the same object.
But if you assign a value to a property of the object, the property will be set/created on the object itself, not its prototype, and hence is not shared:
console.log(a.hasOwnProperty('array')); // prints false
console.log(a.array); // prints ["bar"]
a.array = ['foo'];
console.log(a.hasOwnProperty('array')); // prints true
console.log(a.array); // prints ["foo"]
console.log(b.array); // prints ["bar"]
If you want to create own arrays for each instance, you have to define it in the constructor:
function Foo() {
this.array = [];
}
because here, this
refers to the new
object that is generated when you call new Foo()
.
The rule of thumb is: Instance-specific data should be assigned to the instance inside the constructor, shared data (like methods) should be assigned to the prototype.
You might want to read Details of the object model which describes differences between class-based vs. prototype-based languages and how objects actually work.
Update:
You can access the prototype of an object via Object.getPrototypeOf(obj)
(might not work in very old browsers), and Object.getPrototypeOf(a) === Object.getPrototypeOf(b)
gives you true
. It is the same object, also known as Foo.prototype
.
Javascript: two different objects sharing an array property?
With reference-type properties, each child gets a reference to the same object. Any change any child makes to the object are visible to all instances.
You need to either implement a constructor to set up the property, or have the code that uses the property set it up the first time through. (If you want to use a constructor and Object.create
, though, you'll have to call it yourself; Object.create
won't call it for you.)
You could do something like this...
TestThing = {
code: "?",
intlist: null,
addint : (i) => {
if (!this.intlist) this.intlist = [];
alert("Adding " + i + " to " + this.code + ", list had " + this.intlist.length + " ints");
this.intlist.push(i);
}
}
Or, less error-prone-ly (albeit forsaking Object.create
)...
class TestThing {
constructor(code) {
this.code = code;
this.intlist = [];
}
addint(i) {
alert("Adding " + i + " to " + this.code + ", list had " + this.intlist.length + " ints");
this.intlist.push(i);
}
}
var thing1 = new TestThing("Thing 1");
var thing2 = new TestThing("Thing 2");
thing1.addint(11);
thing2.addint(42);
alert(thing2.intlist); // will output 42
Unfortunately, if you're coding for web browsers, IE (even IE 11) doesn't seem to support class
. So you'll have to stick with the old way of defining classes.
TestThing = function(code) {
this.code = code;
this.intlist = [];
};
TestThing.prototype = {
addint: function(i) {
alert("Adding " + i + " to " + this.code + ", list had " + this.intlist.length + " ints");
this.intlist.push(i);
}
};
When I add and array to an object using prototype in Javascript, all instances of the object act like they're sharing one array
If you are fuzzy about prototype inheritance, make sure you read up on it - it will help you reason about this problem. In essence, an object inherits all properties of its prototype, but an object's own properties has higher precedence than those of its prototype. In your example, TEST.testObj.prototype
is the prototype of both t1
and t2
. Thus, both t1
and t2
inherit its properties.
When the Test.testObj
constructor is called, it then calls the initialize
method, which then sets the object's a
property to the passed-in parameter. This operation will create an a
property on the object itself (it's own property), this will override the inherited a
property on Test.testObj.prototype
. Thus, each time a constructor is called, the new object will get its own a
property.
The second statement in initialize
this.b[0] = a;
is different in nature. It first looks up property b
on the object. Since it is not found on the object itself, it goes up to its prototype Test.testObject.prototype
and finds it there. Now, having a handle on the array object, it then modifies its zeroth element. As you can see, each time the constructor gets called, this same array will be modified.
JavaScript Pushing Array to Class prototype issue
Because t.item is being a refference to test.prototype.item.
So new objects will inherit the item array.
You can avoid by doing
var test = function(){this.item=[];};
That will create a new array on the object itself, instead of using the same array on the prototype for all instances.
Array defined in prototype shared across instances
You implement was not correct yet. Because you put the items
, itemCount
and position
in prototype
. Prototype
is the shared memory of instances
of Class
.
one instanceof ItemController // true
two instanceof ItemController // true
one._proto__ // prototype object of class, included items.
two._proto__ // prototype object of class, included items.
one.items === two.items === ItemController.prototype.items // true, because two of this are pointed to the same reference. It's ItemController.prototype.items
For fixing your issue: put those attributes that you do not want to share in each instant in constructor. The code should be like this:
function ItemScroller() {
this.items = [];
this.itemCount = 0;
this.position = 0;
};
ItemScroller.prototype = {
InsertItem: function( item ) {
},
DisplayPosition: function() {
},
NextItem: function() {
},
PreviousItem: function() {
},
};
So, those attributes above will be created for each instance.
one instanceof ItemController // true
two instanceof ItemController // true
one.items === two.items // false
one.__proto__ // prototype of class, now not included item, itemCount, position...
Why PrototypeJS class instances share same properties?
Every instance shares the same this.settings
object. Mutable data structures have to be initialized in the constructor, not on the prototype:
Test.prototype = {
initialize: function(options) {
this.settings = {
a: {},
b: {},
c: options.c;
};
}
};
Note that this appears to be the "legacy way" (according to the documentation) and the newer way is:
var Test = Class.create({
initialize: function(options) {
this.settings = {
a: {},
b: {},
c: options.c;
};
}
});
This issue is actually addressed in the PrototypeJS documentation:
Types of inheritance in programming languages
Generally we distinguish between class-based and prototypal inheritance, the latter being specific to JavaScript.
Prototypal inheritance, of course, is a very useful feature of the language, but is often verbose when you are actually creating your objects. This is why we emulate class-based inheritance (as in the Ruby language) through prototypal inheritance internally. This has certain implications.
[...]
We can try to do the same in Prototype:
var Logger = Class.create({
initialize: function() { },
log: [],
write: function(message) {
this.log.push(message);
}
});
var logger = new Logger;
logger.log; // -> []
logger.write('foo');
logger.write('bar');
logger.log; // -> ['foo', 'bar']
It works. But what if we make another instance of Logger?
var logger2 = new Logger;
logger2.log; // -> ['foo', 'bar']
// ... hey, the log should have been empty!
You can see that, although you may have expected an empty array in the new instance, it has the same array as the previous logger instance. In fact, all logger objects will share the same array object because it is copied by reference, not by value.
Instead, initialize your instances with default values:
var Logger = Class.create({
initialize: function() {
// this is the right way to do it:
this.log = [];
},
write: function(message) {
this.log.push(message);
}
});
inheritance using prototype in javascript
An explanation would be:
Prototypes are live chains between objects. Because B's prototype is a single tone instance of A, the array prop is passed by reference. This means that for each B instance we have access to the same A instance on the prototype. This might sound confusing at first but that's the magic of prototypes in JavaScript.
This is the caveat of prototypal inheritance. Anything on the prototype will be passed down to each instance. To maintain different instances of the attribute we initialise our object's attributes in the constructor.
When working with objects in JS (like arrays), they are passed by reference.
To make use of JS's prototype system I would suggest something like so:
function A(){
this.text = "";
this.action = function(){
this.text+="z";
this.arr.push(1);
}
this.test = function(){
console.log(this.text+"|"+this.arr);
}
}
function B(){
this.arr = [];
}
B.prototype = new A();
This way we reuse the methods from the "parent" and have a localised array in our "child". At a first glance this might look as a classic OOP inheritance case but it is a bit different.
If you want to read more about prototypes in JS I recommend this article
Hope this helps.
Why the following code, `p.__proto__.aa` does not equal to 200?
When you are trying to access an attribute which does not exist on the object, Javascript will traverse up the prototype chain to find the attribute. However:
p.aa = 200
This assigns the attribute aa
to the object directly. The object now as an attribute .aa
, and it also still has .__proto__.aa
as two independent attributes. You're not indirectly modifying the prototype by assigning to .aa
.
Related Topics
Converting a Js Object to an Array Using Jquery
Best Way to Detect When a User Leaves a Web Page
How to Highlight Text Using JavaScript
Display Data Streamed from a Flask View as It Updates
Indexof Method in an Object Array
Evaluating a String as a Mathematical Expression in JavaScript
JavaScript - Track Mouse Position
How Does This Object Method Definition Work Without the "Function" Keyword
JavaScript: Object Literal Reference in Own Key'S Function Instead of 'This'
Change the Url in the Browser Without Loading the New Page Using JavaScript
Does JavaScript Have "Short-Circuit" Evaluation
Is It Spread "Syntax" or the Spread "Operator"
Run Greasemonkey Script on the Same Page, Multiple Times