Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

javascript prototype inheritance - shared property

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.

like image 653
opio Avatar asked Mar 16 '13 13:03

opio


Video Answer


2 Answers

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]
like image 191
Bergi Avatar answered Nov 01 '22 00:11

Bergi


 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

like image 31
a better oliver Avatar answered Nov 01 '22 01:11

a better oliver