1

I'm trying to extend one constructor with another, using prototype:

var objA = function(name){

    var obj = this; 

    this.test.name = name;

    window.setTimeout(function(){
        console.log(obj.test.name)
    }, 1)

}

var objB = function(name){

    this.name = 'test'


}

objA.prototype.test = new objB();

var a = ['A', 'B', 'C', 'D']

for(var i = 0; i < a.length; i++){
   new objA(a[i])
}

This approach works great for one object, but if ( as in this example ) i want to create multiple, it seems that the last entry ( 'D' ) overwrites previous, because in all 4 cases obj.test.name returns D. Maybe someone could point out what i'm doing wrong, or maybe other solution for this case. Thanks.

Benas R.
  • 13
  • 4

4 Answers4

4

JavaScript implements inheritance through chaining objects. objA has a prototype property test which is an instance of objB. This is shared to all instances of objA

objA.prototype.test = new objB();

Now in your constructor for objA, it modifies objA.prototype.test which is shared across all instances of objA. This means that all instances of objA will have a value of "D" since the last iteration makes the shared property hold "D".

If you want to hold a unique name property for each instance, you need to attach it to the instance, not the shared parent.

var objA = function (name) {
  this.name = name;
}

Now, you seem to notice that there is name on both instance, and the shared parent. Well, JS reads from the instance first. If it sees a property name, it takes it's value from there. If not, it reads from the shared parent, where it defaults to 'test'.

It can be seen here, where I made a minor console.log on them. You can see that the value has 2 name properties, one on the instance, on on the parent, but it reads the instance's value first.

Joseph
  • 117,725
  • 30
  • 181
  • 234
  • It gets a lot more confusing, when re assigning the prototype of an instance directly it only affects the instance instanceOfA.test=[1,2,3] would not affect the test property of other instances of A. But if A would have an array for a prototype like A.prototype.anArray=[] then instanceOfA.anArray.push("22") will affect other instances. So directly assigning a new value to the prototype **.test=** will not affect other instances but directly assigning a property of the prototype **.test.name=** will affect other instances. – HMR May 10 '13 at 09:06
  • 1
    You can inherrit from other functions by having the follwoing in your objA body: objB.call(this) that will take all of the variables defined in the objB function body as **this.something=** part of objA It's not the same as prototyping as prototyping is more like static function/property where all instances share the prototype and setting the objA.prototype=new objB() then later do objB.prototype.somethingNew=... then all instances of objA and objB will have somethingNew – HMR May 10 '13 at 09:15
  • @HMR The case of arrays is only if [the instance has no property of the same name](http://jsfiddle.net/mpxjQ/2/). Otherwise, it will only affect the instance. – Joseph May 10 '13 at 09:15
  • The name on both are by mistake, there should be only for objA, but that doesn't change a lot. I think i got it now, basically objB will always be shared in all instances of objA, there for it can't hold any unique data, because it will get overwritten by other instances. Thanks a lot. – Benas R. May 10 '13 at 09:18
  • @BenasR. Data should only be carried by the instances. It's usually discouraged to share the data, unless you know what you are doing. It's the Functions that are usually shared. – Joseph May 10 '13 at 09:19
  • And immutable data with default values, that way it has it's default value from the prototype and can be re assigned in the instance – HMR May 10 '13 at 09:21
  • @BenasR. Try to run the code from this post in your console and see if you understand why instance properties (can be functions too) are defined with this. and shared (static) are defined with prototype http://stackoverflow.com/questions/16063394/prototypical-inheritance-writing-up/16063711#16063711 That might help a lot in future JS programming. – HMR May 10 '13 at 09:23
1

Prototype is like static functions, all instances share one so all instances of objA share a test property that points to the same instance of objB

Normally assigning new values to a property would set that value for the the instance property: Prototypical inheritance - writing up

In this case you assing a value to a property of the prototype and not the prototype so it'll assign a new value for all instances.

var objA = function(name){
    var obj = this; 
    this.test.name = name;
    console.log("this.name.is:",this.test.name);
}

var objB = function(name){
    this.name = 'test'
}
objA.prototype.test = new objB();
objA.prototype.arr=[];

var a = ['A', 'B', 'C', 'D']
var arr=[];
for(var i = 0; i < a.length; i++){
   arr.push(new objA(a[i]))
}
console.log(arr[0].test.name)
arr[0].arr.push("pushed in 0");
console.log(arr[1].arr);
arr[0].arr=["assigned in 0"];
console.log(arr[1].arr);
arr[0].test.name="assigned in 0";
console.log(arr[1].test.name);
Community
  • 1
  • 1
HMR
  • 37,593
  • 24
  • 91
  • 160
0

Your problem is setTimeout. You're creating an event handler that runs on the last known information, in this case, when the function is passed 'D'. Without setTimeout, it gives the right values.

You probably want to wrap new objA(a[i]) in a setTimeout instead.

window.setTimeout(function(a){   
 new objA(a[i]);
}(a), 1);

Because of closures.

  • I see, i added setTimeout just to simulate condition of an event within the object, because in the real solution, i want to add hash change event on each objA instance, but i had the same problem, that all data passes into that event handler was equal to the last object ( D ). Is there a way around this? – Benas R. May 10 '13 at 08:52
  • Look at the answer Joseph posted. –  May 10 '13 at 08:53
-1

You should make a "createTest" function rather than a "test" object in the prototyp

simonleung
  • 44
  • 2