Jared Kells Jared Kells - 5 months ago 21
Javascript Question

How are properties shared across instances using javascript prototypes

My understanding of prototypical inheritance is every object has a prototype property. If a property doesn't exist on an object then it's prototype object is checked, so on and so on up the chain.

In this example my prototype object is a simple object with a counter property.

I'm expecting every instance to share the same prototype but they appear to get new instances. The output of this code is 00, I was expecting 01.

Am I missing something obvious?

"use strict";

var ConstructorFunction = function () {};

ConstructorFunction.prototype = {
counter: 0,
count: function () {
return this.counter++;
}
};

var a = new ConstructorFunction();
var b = new ConstructorFunction();

$("#output").append(a.count());
$("#output").append(b.count());


Here is the jsfiddle: http://jsfiddle.net/hdA6G/5/

Answer

It is true that the prototype properties are shares across all instances. The problem is that you never change the prototype property. Take a look at your fiddle with some extra logs:

"use strict";

var ConstructorFunction = function () {};

ConstructorFunction.prototype = {
    counter: 0,
    count: function () {
        return ++this.counter;
    }
};

var a = new ConstructorFunction();
var b = new ConstructorFunction();

$("#output").append(a.hasOwnProperty("counter") + " "); //false
a.count();
$("#output").append(a.hasOwnProperty("counter") + " "); //true

As you see, as soon as you call ++this.counter, a local property will be created which will be used from that on.

I assume that this is what happens:

++this.counter is interpreted as this.counter = this.counter + 1. First, the part right of the equal sign is evaluated and since your instance doesn't have a counter property, the counter property of the prototype is used. The value of this property will be added to 1 and then assigned to this.counter, which now creates a local property, in the same way that it does, when you assign a property that hasn't been there at all, like a.xy = 1. xy will be a local property of the instance in that case.

EDIT

There are two workarounds that still would let you use the prototype property:

1) explicitly set the prototype property inside of the count method:

ConstructorFunction.prototype.count = function() {
    return ++this.constructor.prototype.counter;
};

2) call the count method with apply and use the prototype as context:

a.count.apply(a.constructor.prototype);

BUT, if you set the prototype property the way you did, you will get problems with both of these methods.

 ConstructorFunction.prototype = {
     //...
 };

This overrides the complete prototype object and therefore also the constructor property of it. The constructor property will now point to the next higher object in the prototype chain, the Object object. To fix that, you could either set the constructor manually after you assigned the prototype object:

ConstructorFunction.prototype.constructor = ConstructorFunction;

or assign every property of the prototype object seperately:

ConstructorFunction.prototype.counter = 0;
ConstructorFunction.prototype.count = function () {
    return ++this.counter;
};
Comments