JavaScript Object Members That Are Prototyped as Arrays Become Shared by All Class Instances

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



Leave a reply



Submit