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: /
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/hdA6G/5/
Share Improve this question edited Jun 4, 2013 at 6:50 Jared Kells asked Jun 4, 2013 at 6:26 Jared KellsJared Kells 7,0624 gold badges40 silver badges44 bronze badges 3- You have the logic inverted. The property should be unique to the instance and the method should be in the prototype to be shared. – elclanrs Commented Jun 4, 2013 at 6:33
- I'm doing that deliberately. I want the counter property shared across all instances. – Jared Kells Commented Jun 4, 2013 at 6:37
- I see, check my answer, a closure is what you need here. – elclanrs Commented Jun 4, 2013 at 6:54
5 Answers
Reset to default 4It 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 plete 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;
};
Consider using a closure so you can keep that variable private and shared across instances:
var Class = (function ClassModule() {
var counter = 0;
function Class() {}
Class.prototype = {
count: function() {
return counter++;
}
};
return Class;
}());
var class1 = new Class();
var class2 = new Class();
console.log(class1.count()); //=> 0
console.log(class2.count()); //=> 1
The counter
variable is kept in context with the closure (module pattern), and won't be duplicated with each new instance.
In prototype based OOD, sharing means to share the same definition and use it for different contexts. If you need a static property to be shared between contexts you can do as given below
var ConstructorFunction = function (context) {
this.count = function () {
return context + "," + (++this.counter) + "," + (++ConstructorFunction.staticCounter);
};
};
ConstructorFunction.prototype.counter = 0;
ConstructorFunction.staticCounter = 0;
var context1 = new ConstructorFunction("context1");
var context2 = new ConstructorFunction("context2");
$("#output").append(context1.count());
$("#output").append(" ");
$("#output").append(context2.count());
http://jsfiddle/hdA6G/1/
and a better way to define it
var ConstructorFunction = function (context) {
this.context = context;
this.counter = 0;
};
ConstructorFunction.staticCounter = 0;
ConstructorFunction.prototype.count = function () {
return this.context + "," + (++this.counter) + "," + (++ConstructorFunction.staticCounter);
};
http://jsfiddle/hdA6G/3/
You have your inheritance backwards, the prototype inherits from the base object. I think this is more similar to what you are trying to acplish.
function MyObject () {
this.count = 0;
};
ConstructorFunction.prototype = {
counter: function () {
return this.count++;
},
print: function () {
return this.count;
}
};
Var Foo = new MyObject();
console.log(Foo.counter()); // 1
console.log(Foo.counter()); // 2
console.log(Foo.print()); // 2
I hope this helps.
edit
If you want your counter shared across all instances then it should go in you base object.
function MyObject () {
this.count = 0;
this.counter = function () {
return this.count++; //may not need the this
}
};
I know this is an old post, but I am new to Javascrpt and while experimenting I came across something similar, so wanted to put in my 2 cents.
It seems every object gets its own copy of variables declared in prototype once it is instantiated using new
. From then on, accessing using this
will do the usual look up and find the private copy which is operated upon.
However, if you create a prototype variable after creation of all objects, that variable will be shared and behaves like static. I think it is fairly simple to reason why this happens, but, nonetheless, I found this to be an interesting hack. I am not sure if this is a bug in technical specs that might be resolved in future versions, or side effect of standard behavior, so don't know if this is reliable. I am not even sure if this is a newly introduced 'feature' in later versions of the language. Actually, I started googling about this fact and came across this post.
Try this code.
var Worker = function (name) {
this.name = name;
}
Worker.prototype.jobs = 0;
Worker.prototype.Work = function () {
console.log("jobs finished", this.name, ++this.jobs);
}
Worker.prototype.WorkCount = function () {
console.log("work count", this.name, this.workCount);
}
var ca = new Worker("ca");
var cb = new Worker("cb");
ca.Work();// shows 1
cb.Work();// shows 1
ca.WorkCount();// shows undefined
cb.WorkCount();// shows undefined
Worker.prototype.workCount = 2;
ca.WorkCount();// shows 2
cb.WorkCount();// shows 2