According to Douglas Crockford I could use something like http://javascript.crockford.com/prototypal.html (with a little bit of tweaking)... but I am interested in jQuery way of doing it. Is it good practice using $.extend ?
I have 4 classes :
var A = function(){ }
A.prototype = {
name : "A",
cl : function(){
alert(this.name);
}
}
var D = function(){}
D.prototype = {
say : function(){
alert("D");
}
}
var B = function(){} //inherits from A
B.prototype = $.extend(new A(), {
name : "B"
});
var C = function(){} //inherits from B and D
C.prototype = $.extend(new B(), new D(), {
name : "C"
});
var o = new C();
alert((o instanceof B) && (o instanceof A) && (o instanceof C)); //is instance of A, B and C
alert(o instanceof D); //but is not instance of D
So, i can call every method, property ... from A, B, C and D. Problem comes, when I want to test if o is instance of D? How can I overcome this problem?
Is it good practice using $.extend
$.extend is useful for singletons but for prototypes is not ideal.
Using Object.create (or Crockford's polyfill) you can easily create classes like this. I'm using $.extend to simply process the properties and give them default values and the module pattern to keep it well organized. Hope this helps:
// Helper that makes inheritance work using 'Object.create'
Function.prototype.inherits = function(parent) {
this.prototype = Object.create(parent.prototype);
};
var Person = (function PersonClass() {
var _defaults = {
name: 'unnamed',
age: 0
};
function Person(props) {
$.extend(this, props, _defaults);
}
Person.prototype = {
say: function() {
return 'My name is '+ this.name;
}
};
return Person;
}());
var Student = (function StudentClass(_super) {
Student.inherits(_super); // inherit prototype
var _defaults = {
grade: 'untested'
};
function Student(props) {
_super.apply(this, arguments); // call parent class
$.extend(this, props, _defaults);
}
Student.prototype.say = function() {
return 'My grade is '+ this.grade;
};
return Student;
}(Person));
var james = new Student({ name: 'James', grade: 5 });
console.log(james instanceof Student); // true
console.log(james instanceof Person); // true
An object has only one prototype, so you cannot make it an instance of two other types with one call.
$.extend(new B(), new D(), ... creates an object that is an instance of B. Then all properties of D are copied to the newly created object. But the object will still be an instance of B.
Using $.extend is neither good nor bad per se. But you are bound to jQuery, which makes your code less reusable. And you have to be aware of the fact that $.extend overwrites properties with the same name, which might or might not be what you want.
Related
I am playing with Typescript and trying to understand the compiled Javascript code generated by the compiler
Typescript code:
class A { }
class B extends A { }
Generated Javascript code:
var __extends = (this && this.__extends) || (function () {
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var A = /** #class */ (function () {
function A() {
}
return A;
}());
var B = /** #class */ (function (_super) {
__extends(B, _super);
function B() {
return _super !== null && _super.apply(this, arguments) || this;
}
return B;
}(A));
The Javascript inheritance as per Mozilla docs is this:
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;
The parts that I do not understand in the Typescript's generated code are this
1. What is the purpose of this line? Looks like it is copying all the keys of A into B? Is this some sort of a hack for static properties?
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
2. What is this doing?
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
I don't understand this part: (__.prototype = b.prototype, new __())
Why does function B() return this?
return _super !== null && _super.apply(this, arguments) || this;
If someone can explain this to me line by line I would be grateful.
I was curious about this myself and couldn't find a quick answer so here is my breakdown:
What it does
__extends is a function that simulates single class inheritance in object oriented languages and returns a new constructor function for a derived function that can create objects that inherit from a base object.
Note 1:
I wasn't actually aware of this myself but if you do something like the following where all of the values are truthy the variable is set to the value of last item being tested unless one is falsy in which case the variable is set to false:
// value1 is a function with the definition function() {}
var value1 = true && true && function() {};
// value2 is false
var value2 = true && false && function() {};
// value3 is true
var value3 = true && function() {} && true;
I mention this because this is the thing that confused me the most when I saw this javascript and it is used a couple of times in the __extends function defintion.
Note 2:
Parameter d (probably stands for derived) and b (probably stands for base) are both constructor functions and not instance objects.
Note 3:
prototype is a property of a function and it is a prototype object used by 'constructor' functions (i.e. objects created by using new <function name>()).
When you use the new operator to construct a new object, the new object's internal [[PROTOTYPE]] aka __proto__ is set to be the function's prototype property.
function Person() {
}
// Construct new object
var p = new Person();
// true
console.log(p.__proto__ === Person.prototype);
// true
console.log(Person.prototype.__proto__ === Object.prototype);
It isn't a copy. It IS the object.
When you create a literal object like
var o = {};
// true
console.log(o.__proto__ === Object.prototype);
the new object's __proto__ is set to Object.prototype (the built-in Object constructor function).
You can set an object's __prototype__ to another object however using Object.create.
When a property or method isn't found on the current object the object's [[PROTOTYPE]] is checked. If it isn't found then THAT object's prototype is checked. And so it goes checking prototypes until it reaches the final prototype object, Object.prototype. Keep in mind nothing is a copy.
Note 4
When simulating inheritance in Javascript the 'constructor' functions' prototypes are set.
function Girl() {
}
Girl.prototype = Object.create(Person.prototype);
// true
console.log(Girl.prototype.__proto__ === Person.prototype);
// true
console.log(Girl.constructor === Function);
// Best practices say reset the constructor to be itself
Girl.constructor = Girl;
// points to Girl function
console.log(Girl.constructor);
Notice how we point the constructor to Girl because it Person's constructor points to the built-in Function.
You can see the code above in action at: http://jsbin.com/dutojo/1/edit?js,console
Original:
var __extends = (this && this.__extends) || (function () {
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
The break down:
var __extends = (this && this.__extends) || (function () {
// gobbledygook
})();
Keeping my Note 1 above in mind, this first part (and end) is creating a variable called __extends that has the intention of holding a function to set the prototype of the derived class.
(this && this.__extends)
is doing what my Note 1 explains. If this is truthy and this.__extends is truthy then the variable __extends is already exists and so set to the existing instance of itself. If not it is set to what comes after the || which is an iife (immediately invoked function expression).
Now for the gobbledygook which is the actual definition of __extends:
var extendStatics = Object.setPrototypeOf ||
A variable named extendStatics is set to either the built in Object.setPrototypeOf function of the environment that the script is running in (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf)
OR
it creates its own version
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
In Note 3 I discussed __proto__ aka [[PROTOTYPE]] and how it could be set. The code
{ __proto__: [] } instanceof Array
is a test to determine whether the current environment allows setting this property by comparing a literal object's __proto__ set to a literal array with the Array built-in function.
Referring back to my Note 1 from above and keeping in mind that the javascript instanceof operator returns true or false (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof) if the environment evaluates an an object with its prototype property set to the built in Array then extendsStatics is set to
function (d, b) { d.__proto__ = b; })
If the environment doesn't evaluate it that way then extendStatics is set to this:
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }
It does this because __proto__ was never part of the official ECMAScript standard until ECMAScript 2015 (and according to https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto) is there only for backwards compatibility). If it is supported the __proto__ function is used or else it uses the 'roll your own' version' which does a copy from object b to d for user defined properties.
Now that the extendStatics function variable defined, a function that calls whatever is inside extendStatics (as well as some other stuff) is returned. Note that parameter 'd' is the sub-class (the one inheriting) and 'b' is the super-class (the one being inherited from):
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
Breaking it down extendStatics is called and the first parameter object (d) has its prototype set to be (b) (recall Note 3 above):
extendStatics(d, b);
In the next line a constructor function named '__' is declared that assigns its constructor to be the derived (d) constructor function:
function __() { this.constructor = d; }
if the base (b) constructor function happens to be null this will make sure that the derived will keep its own prototype.
From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor, the Object.prototype.constructor (all objects are Objects in javascript):
Returns a reference to the Object constructor function that created
the instance object. Note that the value of this property is a
reference to the function itself, not a string containing the
function's name.
And
All objects will have a constructor property. Objects created without
the explicit use of a constructor function (i.e. the object and array
literals) will have a constructor property that points to the
Fundamental Object constructor type for that object.
So if constructor function '__' was new'd up as is it would create a derived object.
Lastly there is this line:
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
which sets the prototype of the derived (d) to be a new empty object if the base constructor function happens to be null
// b is null here so creates {}
Object.create(b)
OR
sets the __ constructor function's prototype to be the base class prototype and then calls __() which has the effect of setting the derived functions constructor to be the derived function.
(__.prototype = b.prototype, new __()
So basically the final function returned creates a derived constructor function that prototypically inherits from the base constructor function.
Why does function B() return this?
return _super !== null && _super.apply(this, arguments) || this;
According to: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply
The apply() method calls a function with a given this value, and
arguments provided as an array (or an array-like object).
Remember B is a constructor function and that is what is being returned in B's definition.
If you had a Person class (constructor function) that accepted a name parameter in the constructor function you could then call a derived Girl class (constructor function) with the girls name as a parameter.
// Base constructor function
function Person(n) {
// Set parameter n to the name property of a Person
this.name = n;
}
function Girl() {
// Call the Person function with same arguments passed to new Girl
Person.apply(this, arguments);
// Set it so all Girl objects created inherit properties and methods from Person
Girl.prototype = Object.create(Person.prototype);
// Make sure the constructor is not set to Person
Girl.prototype.constructor = Girl;
}
var p = new Person("Sally");
var g = new Girl("Trudy");
console.log(p.name);
console.log(g.name);
This can help understand what is really going on in the TypeScript class extender. In fact, the following code contains exactly the same logic, as one can even use it as a substitute of the original code without all the special tricks that made it extremely difficult to read.
The first half only tries to find a browser compatible version for 'setPrototypeOf'
The second half implements inheritance from the base class
Try to substitute the TypeScript __extends function by the following code:
// refactored version of __extends for better readability
if(!(this && this.__extends)) // skip if already exists
{
var __extends = function(derived_cl, base_cl) // main function
{
// find browser compatible substitute for implementing 'setPrototypeOf'
if(Object.setPrototypeOf) // first try
Object.setPrototypeOf(derived_cl, base_cl);
else if ({ __proto__: [] } instanceof Array) // second try
derived_cl.__proto__ = base_cl;
else // third try
for (var p in base_cl)
if (base_cl.hasOwnProperty(p)) derived_cl[p] = derived_cl[p];
// construct the derived class
if(base_cl === null)
Object.create(base_cl) // create empty base class if null
else
{
var deriver = function(){} // prepare derived object
deriver.constructor = derived_cl; // get constructor from derived class
deriver.prototype = base_cl.prototype; // get prototype from base class
derived_cl.prototype = new deriver(); // construct the derived class
}
}
}
In the following version, all the stuff that makes it general-purpose was taken out, such as browser compatibility handling and 'null' derivatives. I would not encourage anyone to put the following code as a permanent substitute, but the version below really demonstrates the bare essence of how class inheritance works with TypeScript.
// Barebone version of __extends for best comprehension
var __extends = function(derived_cl,base_cl)
{
Object.setPrototypeOf(derived_cl,base_cl);
var deriver = function(){} // prepare derived object
deriver.constructor = derived_cl; // get constructor from derived class
deriver.prototype = base_cl.prototype; // get prototype from base class
derived_cl.prototype = new deriver(); // construct derived class
}
Try the following working example:
var __extends = function(derived_cl,base_cl)
{
Object.setPrototypeOf(derived_cl,base_cl);
var deriver = function(){} // prepare derived object
deriver.constructor = derived_cl; // get constructor from derived class
deriver.prototype = base_cl.prototype; // get prototype from base class
derived_cl.prototype = new deriver(); // construct derived class
}
// define the base class, and another class that is derived from base
var Base = function()
{
this.method1 = function() { return "replace the batteries" }
this.method2 = function() { return "recharge the batteries" }
}
var Derived = function(_super) {
function Derived() {
__extends(this, _super); _super.apply(this, arguments);
this.method3 = function() { return "reverse the batteries" }
this.method4 = function() { return "read the damn manual" }
}
return Derived
}(Base)
// Let's do some testing: create the objects and call their methods
var oBase = new Base(); // create the base object
var oDerived = new Derived(); // create the derived object
console.log(oDerived.method2()); // result: 'recharge the batteries'
console.log(oDerived.method4()); // result: 'read the damn manual'
console.log(oBase.method1()) ; // result: 'replace the batteries'
try{ console.log(oBase.method3()) }
catch(e) {console.log(e.message)}; // result: 'oBase.method3 is not a function'
And finally, when you are tired learning from the obfuscated inheritance mechanism of TypeScript, I discovered that the __extend function is not even necessary, just by letting the native JavaScript function 'apply' do the work, which through prototype chaining exactly implements our aimed inheritance mechanism.
Try this last example ...and forget everything else, or did I miss something ?
// Short, readable, explainable, understandable, ...
// probably a 'best practice' for JavaScript inheritance !
var Base = function()
{
this.method1 = function() { return "replace the batteries" }
this.method2 = function() { return "recharge the batteries" }
}
var Derived = function(){
Base.apply(this, arguments); // Here we inherit all methods from Base!
this.method3 = function() { return "reverse the batteries" }
this.method4 = function() { return "read the damn manual" }
}
var oDerived = new Derived(); // create the derived object
console.log(oDerived.method2()); // result: 'recharge the batteries'
I would like to follow the inheritance structure shown above. I would like create an engineer using this syntax:
var Mark = new Employee(id).WorkerBee(project).Engineer();
To achieve this syntax, I have to create a nested object following a parasitic inheritance pattern like so:
function Employee(id) {
this.id = id;
this.WorkerBee = function(project) {
this.project = project;
this.Engineer = function() {
...
return this;
};
return this;
};
}
To avoid deep layers of nesting, I am trying to rewrite it using prototypes. How can I rewrite my code to achieve the same goal as above ?
function Employee(id) {
//variables
this.id = id
this.name = "";
this.dept = "general";
//methods
this.getId = function() {
return this.id
}
}
Employee.prototype.WorkerBee = WorkerBee;
function WorkerBee(project) {
//variables
this.projectName = project
this.projects = [];
//methods
this.getProjectName = function() {
return this.projectName
}
return this
}
WorkerBee.prototype.Engineer = Engineer
function Engineer() {
//variables
this.dept = "engineering";
this.machine = "";
//methods
this.getDept = function() {
return this.dept
}
return this
}
var Mark = new Employee("5").WorkerBee("Secret Project").Engineer();
console.log(Mark.getId()) //should print "5"
console.log(Mark.getProjectName()) //should print "Secret Project"
console.log(Mark.getDept()) //should print engineering
UPDATE:
Ok, I understand partially. What is the reason why you want to do this? Do you just want a shortcut for creating multiple instances using multiple statements?
Should the instance of C returned by A().B().C() be any different than the one created with the standard new C()?
If you just want to chain constructors, you can add the context in which they are defined (most likely the global object) to the prototype chain of the created entities. You should be able to do that:
var A = function () {};
A.prototype = Object.create(this);
What this does not eliminate though is the need for the new keyword for instantiation. You would need to do new (new (new A()).B()).C(). I can't think of a different approach than having a helper function which would create constructors which do not require the new keyword:
var define = function (init) {
var Constructor = function () {
if (!(this instanceof Constructor)) {
return new Constructor();
}
if (init) {
init.apply(this, Array.prototype.slice.call(arguments));
}
};
Constructor.prototype = Object.create(this);
return Constructor;
};
The usage is:
var A = define(function (x, y) {
this.x = x;
this.y = y;
});
var a1 = new A(1, 2);
// a1 instanceof A === true
// a1.x === 1
// a1.y === 2
var a2 = A(1, 2);
// a2 instanceof A === true
// a2.x === 1
// a2.y === 2
If you have the constructors A, B and C, you can use the following notations interchangeably:
var a = new A();
var b = new B();
var c = new C();
var a = A();
var b = B();
var c = C();
var b = A().B();
var c = A().C();
var a = B().C().A();
In case of A().B().C(), you do not have access to the instances of A and B.
Can you elaborate a bit more on what is your deal?
OLD ANSWER:
What you have there is madness as you basically merge three constructors and make it seem that `WorkerBee` and `Employee` are actually instantiated while they are not.
I'm not going to question the new A().B().C() notation even though I find it quite messed up.
You probably want to make use of the instanceof operator the following way.
var A = function (x) {
if (!(this instanceof A)) return new A(x);
this.x = x;
};
var B = function (y) {
if (!(this instanceof B)) return new B(y);
this.y = y;
};
var C = function () {
if (!(this instanceof C)) return new C();
};
A.prototype.B = B;
B.prototype.C = C;
You can now call new A() and A(), new B() and B() and new C() and C() interchangeably while achieving the same result as both of the calls always return an instance of the constructor.
new A() instanceof A === true
new A().B() instanceof B === true
new A().B().C() instanceof C === true
Based on the comments, you seem to have the belief that you have to use this odd, verbose mechanism in order to have inheritance between Employee <- WorkerBee <- Engineer (and such), but you don't; normal inheritance is all you need for this:
// ==== Employee
function Employee(id) {
this.id = id;
}
// Add Employee methods to Employee.prototype, e.g.:
Employee.prototype.getId = function() {
return this.id;
};
// ==== WorkerBee, derived from Employee
function WorkerBee(id, project) {
// Inheritance, part 1: Chain to the base constructor
Employee.call(this, id);
// WorkerBee stuff
this.project = project;
}
// Inheritance, part 2: Create the object to use for WorkerBee
// instance prototypes, using Employee.prototype as its prototype.
WorkerBee.prototype = Object.create(Employee.prototype);
WorkerBee.prototype.constructor = WorkerBee;
// Add WorkerBee methods to WorkerBee.prototype, e.g.:
WorkerBee.prototype.getProjectName = function() {
return this.project;
};
// ==== Engineer, derived from WorkerBee
function Engineer(id, project) {
// Inheritance, part 1: Chain to the base constructor
WorkerBee.call(this, id, project);
}
// Inheritance, part 2: Create the object to use for Engineer
// instance prototypes, using WorkerBee.prototype as its prototype.
Engineer.prototype = Object.create(WorkerBee.prototype);
Engineer.prototype.constructor = Engineer;
// Add Engineer methods to Engineer.prototype, e.g.:
Engineer.prototype.getDept = function() {
return "Engineering";
};
// ==== Usage
var mark = new Engineer("5", "Secret Project");
snippet.log(mark.getId()); // "5"
snippet.log(mark.getProjectName()); // "Secret Project"
snippet.log(mark.getDept()); // "Engineering"
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
That's the standard way to do prototypical inheritance using constructor functions in JavaScript (for now; in ES6, you'd use the new class feature, which does basically the same thing, with some syntactic sugar). Just add Manager (derived from Employee) and SalesPerson (derived from WorkerBee).
On old browsers, you may need a partial polyfill for Object.create, which looks like this:
if (!Object.create) {
Object.create = function(proto, props) {
if (typeof props !== "undefined") {
throw "The two-argument version of Object.create cannot be polyfilled.";
}
function ctor() { }
ctor.prototype = proto;
return new ctor();
};
}
There are transpilers for taking ES6 source with class and turning it into ES5 code. There's also my Lineage script, which makes inheritance a lot less verbose.
I am working on implementing a simple Class in JavaScript. I know there are many great libraries out there that already does this. However, my intent is to understand the JavaScript prototype model better. Anyhow, I wrote the following. It is working as far as I can tell but with one catch I will describe later:
function Class(constructor,prototypeObj){
var c = constructor;
c.create = function(){
//var o = new constructor();
var o = Object.create(c.prototype);
c.apply(o,arguments);
return o;
};
c.prototype = prototypeObj;
return c;
}
var Animal = Class(
function Animal(life){
this.life = life;
},
{
type: "animal",
sayLife: function(){
console.log(this.life);
}
}
);
var o = Animal.create(15);
console.log(o instanceof Animal); // True
console.log(o.sayLife()); // 15
console.log(o.type); // "animal"
However, my problem is that when I input in o (for the instance) in the console; it prints
Object {life: 15, type: "animal", sayLife: function}life: 15 __proto__: Object
But I would like it to be
Animal {life: 15, type: "animal", sayLife: function}life: 15 __proto__: Object
If I change line 4 and 5 in the constructor.create method to use new constructor() instead of Object.create(constructor.prototype), I get the desired behaviour but then I have issues passing in arguments to the constructor when initializing a new object. If I return a function other than the constructor passed into the class function, then the objects names would not be Animal but something else like "Object" or "F".
So my question is is there any way to implement the Class function such that the instances of the Class always share the same name as the constructor function?
function Class(constructor, prototypeObj) {
var c = constructor;
c.create = function() {
//var o = new constructor();
var o = Object.create(c.prototype);
c.apply(o, arguments);
return o;
};
c.prototype = prototypeObj;
c.prototype.constructor = constructor;
return c;
}
var Animal = Class(
function Animal(life) {
this.life = life;
}, {
type: "animal",
sayLife: function() {
console.log(this.life);
}
}
);
var o = Animal.create(15);
console.log(o);
If you simply want to change the type of the Object being printed in the console, then you need to adjust the constructor function's prototype property's constructor property, like this
c.prototype = prototypeObj;
c.prototype.constructor = constructor;
return c;
Did you know that the constructor function is not really a special function? It's just like any other method on the prototype. So you don't need to treat it any differently from other prototype methods. This is what I usually do:
function defclass(prototype) {
var constructor = prototype.constructor;
constructor.prototype = prototype;
return constructor;
}
This allows me to create constructors as follows:
var Animal = defclass({
constructor: function Animal(life) { // note that I named the constructor
this.life = life;
},
sayLife: function () {
console.log(this.life);
},
type: "animal"
});
Then we can use it as follows:
var o = new Animal(15);
console.log(o instanceof Animal); // true
console.log(o.sayLife()); // 15
console.log(o.type); // "animal"
The reason your code didn't work as expected was because you forgot to reset the constructor property on the prototype. However using my method, since the constructor method is already a property of prototype, you don't have to do anything else.
For more information about prototypal inheritance see the following question:
JavaScript inheritance and the constructor property
You might want to use Object.create()
The Object.create() method creates a new object with the specified prototype object and properties.
This allows you to fully configure an object when creating, including the possibility of specifiying any constructor:
var o = Object.create(Animal.prototype,{
constructor:{
value: Animal
}
})
Edit:
See #dfsq 's answer and the comments below for an even simpler solution.
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.
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]
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
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.
Given this bit of code:
var SuperClass = function(a, b) {
this.a = a;
this.b = b;
};
SuperClass.prototype.getA = function() {
return this.a;
};
var SubClass = function(a, b, c) {
SuperClass.call(this, a, b);
this.c = c;
};
In order to initiate the SubClass prototype, most recommendations seem to be the following:
SubClass.prototype = new SuperClass();
It seems odd to me to create (instantiate) a new SuperClass object (with its own a and b properties) just to serve as the prototype for the SubClass.
This also works:
// anti-pattern
SubClass.prototype = SuperClass.prototype;
but it passes the SuperClass.prototype object by reference, so anything you add to the SubClass.prototype is also added to the SuperClass.prototype, because they are the same object. This is not expected behavior in most cases.
QUESTION: Is there a way to achieve the proper prototyping without creating an instance of the SuperClass to serve as the SubClass's base prototype?
Under modern browsers:
SubClass.prototype = Object.create( SuperClass.prototype );
This lets you create an object with a defined __proto__ without invoking the 'constructor' method of the parent class. For more details, read up on Object.create (including a polyfill implementation for older browsers).
Seen in action:
function Foo(){ console.log("AHHH!"); }
Foo.prototype.foo = 42;
function Bar(){}
Bar.prototype = Object.create(Foo.prototype); // Note: no "AHHH!" shown
Bar.prototype.bar = 17;
// Showing that multi-level inheritance works
var b = new Bar;
console.log(b.foo,b.bar); //-> 42, 17
// Showing that the child does not corrupt the parent
var f = new Foo; //-> "AHHH!"
console.log(f.foo,f.bar); //-> 42, undefined
// Showing that the inheritance is "live"
Foo.prototype.jim = "jam";
console.log(b.jim); //-> "jam"
Polyfill implementation for older browsers
// Create a new instance based on existing object
function object(o) {
function F() {};
F.prototype = o;
return new F();
}
function inherit(subClass, superClass) {
var prototype = object(superClass.prototype);
prototype.constructor = subClass;
subClass.prototype = prototype;
}
Although pass superClass.prototype leads to F.prototype = superClass.prototype, when object function return, the F function is not be accessed any more. In result, you are not able to get and change F.prototype.
Example:
function SuperClass() {};
function SubClass() {};
SuperClass.prototype.say = function() {console.log('super');};
var instance = object(SuperClass.prototype); //instance.__proto__ === SuperClass.prototype
instance.say();