6

I have saved a property _data in prototype as a definition for all created objects.

 function A() {}
 A.prototype._data = [];

Now all objects created from A have property _data.

I'd like prototype inheritance, where _data of prototype will have _data values from all prototypes in prototype chain.

Don't know direct way, in this example I use a getter get().

 function A() {}

 A.prototype._data = [];

 A.prototype.add = function(rec) {
   this.__proto__._data.push(rec);
 }

 A.prototype.get = function() {
   if(typeof this.__proto__.constructor.prototype.get == 'function')
   {
     return this.__proto__.constructor.prototype.get().concat(this.__proto__._data);
   }
   else
   {
     return this.__proto__._data || [];
   }
 }

 function B() {}
 B.prototype = Object.create(A.prototype, { constructor: { value: B }});
 B.prototype._data = [];

When I create object a with values aa and object b with value bb, b.get() returns [aa, bb]. And later if _data of prototype A will be extended with aaaa, function b.get() returns [aa, aaaa, bb].

 var a = new A(), b = new B();

 a.add('aa');
 b.add('bb');
 console.log(b.get()); // [aa, bb]

 a.add('aaaa');
 console.log(b.get()); // [aa, aaaa, bb]

 // EDITED - _data in A prototype shoud be without B
 console.log(a.get()); // [aa, aaaa]

Is it a good (standard) way how to achieve this? I mean using constructor correction while Object.create and reference parent prototype with constructor.prototype?

Here is a demo: http://jsfiddle.net/j9fKP/

Reason for all of this is field definition for scheme in ORM library, where inheritance of schemes is allowed. Child scheme has to have all fields from parent scheme.

opio
  • 303
  • 1
  • 13
  • possible duplicate of [Javascript object members that are prototyped as arrays become shared by all class instances](http://stackoverflow.com/questions/4425318/javascript-object-members-that-are-prototyped-as-arrays-become-shared-by-all-cla) – Bergi Jun 28 '13 at 03:36

3 Answers3

1

I'd like prototype inheritance, where _data of prototype will have _data values from all prototypes in prototype chain.

That's a different thing. "Prototype inheritance" means that if there's a _data property on the current object, it won't go looking further in the chain. Also, it seems to be a kind of issue with nested objects, though I'm not sure what you really want. However, it hardly will make sense to let an array object inherit from another array, if you actually want to concatenate them.

So I think your getter is really fine.

Is it a good (standard) way how to achieve this? I mean using constructor correction while Object.create and reference parent prototype with constructor.prototype

Constructor correction is nice, but actually quite useless (especially if you expect a standard-conform Object.create).

However, in this.__proto__.constructor.prototype either the .__proto__ or the .constructor.prototype is redundant. Since both are either nonstandard or require constructor correction, you should use the standard Object.getPrototypeOf() function to get your prototype object.

With the following very generic solution, you can nest the inheritance (A.proto, B-proto, B-instance, …) arbitrarily deep. Everything inheriting from A.prototype will have an add method which adds _data to the current object, and a get method that traverses the prototype chain and collects all _data:

function A() {
    // this._data = []; // why not?
}
A.prototype._data = []; // not even explicitly needed
A.prototype.add = function(rec) {
    if (! this.hasOwnProperty("_data")) // add it to _this_ object
        this._data = [];
    this._data.push(rec);
}
A.prototype.addToAllInstances = function(rec) {
    Object.getPrototypeOf(this).add(rec);
}
A.prototype.get = function() {
    var proto = Object.getPrototypeOf(this);
    var base = typeof proto.get == 'function' ? proto.get() : [];
    // maybe better:
    // var base = typeof proto.get == 'function' && Array.isArray(base = proto.get()) ? base : [];
    if (this.hasOwnProperty("_data"))
        return base.concat(this._data); // always get a copy
    else
        return base;
}

function B() {
    A.call(this);
}
B.prototype = Object.create(A.prototype, { constructor: { value: B }});
B.prototype._data = []; // not even explicitly needed

Example usage:

var a = new A();
var b = new B();

a.add('ai');
a.get(); // [ai]

a.addToAllInstances('ap'); // === A.prototype.add('ap');
a.get(); // [ap, ai]
new A().get(); // [ap]
b.get(); // [ap]
b.prototype.get(); // [ap]

b.add('bi');
b.get(); // [ap, bi]

a.addToAllInstances('aap');
b.addToAllInstances('bp');
b.get(); // [ap, aap, bp, bi]
Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Really nice code! with getPrototypeOf and also forgotten instance _data property. Thank you a lot for excellent explanation and example. Exactly what i need. – opio Mar 16 '13 at 14:43
1
 function A() {}
     A.prototype._data = [];

 A.prototype.add = function(rec) {
   this._data.push(rec);
 }

 A.prototype.get = function() {
   return this._data;
 }

 function B() {}
 B.prototype = Object.create(A.prototype, { constructor: { value: B    }});

 B.prototype._data = [];

 B.prototype.get = function() {
   return A.prototype._data.concat(this._data);
 }  

 a.add('aa');
 b.add('bb');
 console.log(b.get()); // [aa, bb]

 a.add('aaaa');
 console.log(b.get()); // [aa, aaaa, bb]

Fiddle

a better oliver
  • 26,330
  • 2
  • 58
  • 66
  • i'm sorry for bad question i edited lately, a.get() should return only [aa, aaaa]. Thank you – opio Mar 16 '13 at 14:47
1

I think I have a better understanding of what you want to do now, so I've deleted my earlier answer and am posting this one.

Here's how I think I'd do it (with the caveat that I'm not at all sure that with an even better understanding, a completely different approach wouldn't be better):

function A() {}
A.prototype._Adata = [];

A.prototype.add = function(rec) {
  this._Adata.push(rec);
};

A.prototype.get = function() {
  return this._Adata;
};

function B() {}
B.prototype = Object.create(A.prototype, { constructor: { value: B }});

B.prototype._Bdata = [];

B.prototype.add = function(rec) {
  this._Bdata.push(rec);
};
B.prototype.get = function() {
  return this._Adata.concat(this._Bdata);
  // Or: return A.prototype.get.call(this).concat(this._Bdata);
};

var a = new A();
var b = new B();

a.add('aa');
b.add('bb');
console.log(b.get()); // [aa, bb]

a.add('aaaa');
console.log(b.get()); // [aa, aaaa, bb]

Fiddle

That way, B isn't reaching too deeply into A's internals.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • Isn't `_Adata` an "A internal"? You could (and imo should) use A's `get`. – Bergi Mar 16 '13 at 14:30
  • @Bergi: Yeah, I guess you could argue that it is. To me, the whole point of these is to share that information, so I don't consider them "internal", and calling `A`'s version of `get` is a pain without a helper library. But I've added it as an option. – T.J. Crowder Mar 16 '13 at 14:32