I'm refreshing my javascript skills and came up with a way that objects can inherit from another.
The inheritance should be tree-like.
Parent->Child->Child2
I've extended the Function.prototype (Don't tell me this is a bad idea, this can be changed later)
Function.prototype.extend = function(child)
{
// save child prototype
var childPrototype = child.prototype;
// copy parent to child prototype
child.prototype = Object.create(this.prototype);
// merge child prototype
for (var property in childPrototype) {
child.prototype[property] = childPrototype[property];
}
child.prototype.constructor = child;
child.prototype.$parent = this.prototype;
return child;
};
The parent object:
var Parent = (function()
{
var Parent = function(x, y)
{
this.x = x;
this.y = y;
console.log('Parent constr', x, y);
}
Parent.prototype.move = function(x, y)
{
this.x += x;
this.y += y;
console.log('Parent moved.');
};
return Parent;
}());
First child:
var Child = Parent.extend(function()
{
var Child = function(x, y)
{
this.$parent.constructor(x, y);
console.log('Child constr');
}
Child.prototype.print = function()
{
console.log('Child print', this.x, this.y);
};
// override
Child.prototype.move = function(x, y)
{
this.$parent.move(x, y);
console.log('Child moved.');
};
return Child;
}());
Second child:
var Child2 = Child.extend(function()
{
var Child2 = function(x, y)
{
this.$parent.constructor(x, y);
console.log('Child2 constr');
}
// override
Child2.prototype.move = function(x, y)
{
this.$parent.move(x, y); // call parent move
console.log('Child2 moved.');
};
return Child2;
}());
So far, so good. I'm able to call the parents constructor and methods and even override methods.
var child = new Child2(1, 1);
child.move(1, 1);
child.print();
I get following output which is correct:
Parent constr 1 1
Child constr
Child2 constr
Parent moved.
Child moved.
Child2 moved.
Child print 2 2
But if I comment out the override from second child I get following output:
Parent constr 1 1
Child constr
Child2 constr
Parent moved.
Child moved.
Child moved. -> WHY??
Child print 2 2
I don't understand why Child moved. is outputted twice. The result is correct but something weird is going on.
EDIT:
Finally after researching and diving more into the problem I came with a nice solution:
// call method from parent object
Object.getPrototypeOf(Child2.prototype).move.apply(this, arguments);
I made another extension to Function:
Function.prototype.getParent = function()
{
return Object.getPrototypeOf(this.prototype);
};
And then for example the Child2 move method:
Child2.prototype.move = function(x, y)
{
Child2.getParent().move.call(this, x, y);
};
So I don't need $parent anymore and I get the desired result.
Another solution would be to call the parent prototype directly:
Child2.prototype.move = function(x, y)
{
Child.prototype.move.call(this, x, y);
};
You should be using apply to invoke your functions in the correct scope, also using this to access the parent is not safe. This is actually an excellent problem for learning about JS debugging, you can see everything happening using break points.
looking at your first Child :
// override
Child.prototype.move = function(x, y)
{
this.$parent.move(x, y);
console.log('Child moved.');
};
When this code is reached by calling child.move(1,1), this points to child, and what does child.$parent.move point to? why, the implementation in the first Child of course!
This second time around, this points to the prototype of the Parent, so now we're fine.
So that's why this is happening, what can you do about it? Well that's up to you, I mentioned apply because that was my default thought, so you would use this.$parent.move.apply(this, arguments);, but then we really can't get access to higher parents, so the only other option I can think of is binding (hitching, for any dojo people reading) every function to it's respective prototype, thereby guaranteeing its this value always points to the same thing, and therefore it can always access the $parent reliably.
Of course, this would mean no access to the actual object instance, so it's only really useful for pure utility functions, but this is actually not new. If you had overridden print (which accesses the object instance's x and y members) and tried to invoke the parent, that would have broken as well.
Your extend method attaches the inherited properties and methods to the new object's prototype. It also creates a $parent object which is equal to the prototype of the parent object. Note that this results in duplication. The second child has three move methods:
child.protoype.move
child.$parent.move
child.$parent.$parent.move
Even if you comment out the override there are still three move methods (although two are references to the same function).
Therefore, when you run child.move you get:
child.prototype.move which invokes child.$parent.move which invokes child.$parent.$parent.move
The fact that child.prototype.move and child.$parent.move are both references to the same function does not prevent them from being executed twice.
Related
Let’s say we have:
var parent = {};
var child = Object.create(parent);
Can parent enumerate children and use their properties and methods somehow?
Yes, I can create an array of children. However, there’s a possibility to forget to do it with another new child.
Disregarding the unorthodox terminology, you can't enforce inheriting instances from adhering to anything and as a result there is no reliable way to do what you seek to do.
Try a Post-It ;-)
You will need some reference. An object does not know its parent automatically. But instead of saving an index, I think you can save the parent object itself. The parent is stored by reference, so if the parent is modified, the parent reference of the child reflects those changes. This is shown below in a slightly altered version of your code:
function parent() {
this.index = 0;
// Make children a property (for this test case) and
// pass 'this' (the parent itself) to a child's constructor.
this.children = [new child(this)];
}
function child(parent) {
// Store the parent reference.
this.parent = parent;
}
// Do this after the functions are declared. ;)
var parents = [new parent()];
// Set a property of the parent.
parents[0].test = "Hello";
// Read back the property through the parent property of a child.
alert(parents[0].children[0].parent.test); // Shows "Hello"
That is not a parent/child relationship.
That's a prototypal-inheritance link.
Prototypal inheritance is not the same.
const a = { x: 1, y: 2, doZ () { } };
const b = Object.create(a);
b.__proto__ === a; // true
The old-fashioned version of this was to do something like:
function A (x, y) {
this.x = x;
this.y = y;
}
A.prototype.doZ = function () { };
var a = new A(1, 2);
function B () { }
B.prototype = a;
var b = new B();
var c = new B();
var d = new B();
Should a be allowed to iterate over all instances of B?
Not really, no.
Say there is a parent class Shape and a child class Rectangle. I want to reuse a parent class property's value from within the child class.
Can I do this without re-initializing it in the child class (using call or apply)?
I want all the child objects to use the same parent property value.
//Parent
function Shape(ctx) {
this.context = ctx;
}
Shape.prototype.getContext = function() { return this.context; };
//Child - rectangle inherits from shape
function Rectangle(x,y,w,h) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
}
//setup inheritance
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;
Rectangle.prototype.draw = function() {
//want to use inherited context here
return this.context;
}
//create and run
var shape = new Shape("value");
var rectangle = new Rectangle(0,0,100,100);
//returns "value"
console.log( shape.getContext() );
//returns undefined - needing "value"
console.log( rectangle.draw() );
EDIT - After the responses below I think this is what I need. Since the rectangle instance is not inheriting from the shape instance the "value" assigned to shape is not being passed to rectangle. How about assigning it as the default inside Shape and then calling the Shape constructor inside the Rectangle constructor. This allows me to share the same context value to all the child objects right?
Side issue, the setter doesn't affect Shape children. So I'm working on that.
//Parent
function Shape() {
this.context = "value";
}
Shape.prototype.getContext = function() { return this.context; };
Shape.prototype.setContext = function(x) { this.context = x; };
//Child - rectangle inherits from shape
function Rectangle(x,y,w,h) {
Shape.call(this);
this.x = x;
this.y = y;
this.w = w;
this.h = h;
}
//setup inheritance
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;
Rectangle.prototype.draw = function() {
//want to use inherited context here
return this.context;
}
//create and run
var rectangle = new Rectangle(0,0,100,100);
//returns "value"
console.log( rectangle.draw() );
EDIT - Thanks to the responses, I think below is accomplishing what I was initially trying to do. The Shape parent begins with a default context value. The Shape constructor now also accepts an argument in case child classes want to change it when initially called. Each child class then has a getter and setter as well for the context, but it will always default to the initial Parent value unless changed. After looking into it more the Shape is starting to feel like an abstract class or an interface, but that has nothing to do with what I was initially asking.
//Parent
function Shape(ctx) {
this.context = (typeof ctx === "undefined") ? "default" : ctx;
}
Shape.prototype.getContext = function() { return this.context; };
Shape.prototype.setContext = function(x) { this.context = x; };
//Child - rectangle inherits from shape
function Rectangle(x,y,w,h) {
//calls parent constructor
Shape.call(this);
this.x = x;
this.y = y;
this.w = w;
this.h = h;
}
//setup inheritance
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;
//getter and setter - context defaults to parent value, but can be changed
Rectangle.prototype.getContext = function() { return this.context; };
Rectangle.prototype.setContext = function(x) { this.context = x; };
//other rectangle methods
Rectangle.prototype.draw = function() {
return "doing something with " + this.context;
}
//create and run
var rectangle = new Rectangle(0,0,100,100);
//starts with Parent "default"
console.log( rectangle.getContext() );
//changes and uses different context
rectangle.setContext("different context");
console.log( rectangle.draw() );
Can I do this without re-initializing it in the child class (using call or apply)?
No, there's no way around calling your super constructor.
Notice that this is not re-initialising the property, the call is creating and initialising the property at all.
I want to reuse a parent class property's value from within the child class.
This doesn't really make sense. The property is part of the instance, not of the class. There is no instance of the parent class if you have multiple children, there are just child instances. If you are really creating an instance of the parent class (like in your example code), then that is a separate object and has nothing to do with the other instances - mostly, the children objects are not in any way derived from that parent instance.
The return value is undefined when you do this to setup your rectangle:
//create and run
var shape = new Shape("value");
var rectangle = new Rectangle(0,0,100,100);
because you are actually not connecting that shape to rectangle. You have merely made two separate instances of Shape and Rectangle which aside from sharing certain structures have nothing to do with each other.
There are possible scenarios to fixing this. Create a new Shape to seed the value with up front when defining the prototype.
//Use predefined Shape
var shape = new Shape("value");
Rectangle.prototype = shape;
Now when the previously shown call to draw is used, value will be returned and logged to the console.
This is not a complete scenario in my opinion though because while you have defined a getter for the context, there may be scenarios where you wish to change that. I would also suggest implementing a setter for the context so that you can inject different ones if you feel like it.
I've been spending my past couple hours researching prototypical inheritance, but I'm left with conflicting/unclear answers on how it should be done. It seems to be working for how I'm using it right now.
Paper.Component = function(){}; // useless unless I can solve problem mentioned below?
Paper.Component.prototype = {
isInBounds: function(x, y){
// ...
},
setPosition: function(x, y){
// ...
},
setSize: function(w, h){
// ...
}
};
Paper.Button = function(x, y, w, h, text){
// ...
}
Paper.Button.prototype = Object.create(Paper.Component.prototype);
It also seems to have another problem; How can I have Paper.Button save it's constructor information (x,y,w,h) onto Paper.Component rather than on itself? I.e., how can every child of Paper.Component inherit and set these values?
What you have so far is fine. The missing bit in Button looks like this:
Paper.Button = function(x, y, w, h, text){
Paper.Component.call(this, /*...any args required by it...*/);
// Button stuff here...
};
Function#call calls a function with a specific this value and any arguments you pass it. So the above calls Paper.Component from within Paper.Button with this referencing the current object, and passing along any appropriate arguments.
You also want to set the constructor property on any prototype you replace rather than just adding to. It's largely optional (JavaScript itself doesn't use constructor for anything), but since the JavaScript engine sets it on the default prototype objects, we should set it when replacing them, just so we're consistent with the default prototypes.
Slightly simpler, concrete example:
function Shape(sides, color) {
this.sides = sides;
this.color = color;
}
// Shape.prototype.method = ...
function Rectangle(color) {
Shape.call(this, 4, color);
// Rectangle stuff here...
}
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle; // <== The constructor bit
// Rectangle.prototype.method = ...
If you're interested in setting up hierarchies of "classes" of objects using prototypical inheritance in JavaScript, you might want to look at my Lineage helper script, which automates the above with a simpler syntax and provides other useful features.
A good reference is the MDN - Inheritance revisited page
What you are looking for (I think) is something like this:
Paper.Component = function(x,y,w,h){
this.setPosition( x, y );
this.setSize( w, h );
};
Paper.Component.prototype.isInBounds = function(x, y){};
Paper.Component.prototype.setPosition = function(x, y){};
Paper.Component.prototype.setSize = function(w, h){};
Paper.Button = function(x, y, w, h, text){
Paper.Component.apply( this, arguments );
}
Paper.Button.prototype = Object.create(Paper.Component.prototype);
Paper.Button.prototype.constructor = Paper.Button;
Things to note:
Do not do Paper.Component.prototype = { ... } as it will overwrite the current prototype (e.g. the existing .prototype.constructor and anything else anyone has already created).
Remember to set the constructor (the last line).
I've been spending my past couple hours researching prototypical inheritance, but I'm left with conflicting/unclear answers on how it should be done. It seems to be working for how I'm using it right now.
Paper.Component = function(){}; // useless unless I can solve problem mentioned below?
Paper.Component.prototype = {
isInBounds: function(x, y){
// ...
},
setPosition: function(x, y){
// ...
},
setSize: function(w, h){
// ...
}
};
Paper.Button = function(x, y, w, h, text){
// ...
}
Paper.Button.prototype = Object.create(Paper.Component.prototype);
It also seems to have another problem; How can I have Paper.Button save it's constructor information (x,y,w,h) onto Paper.Component rather than on itself? I.e., how can every child of Paper.Component inherit and set these values?
What you have so far is fine. The missing bit in Button looks like this:
Paper.Button = function(x, y, w, h, text){
Paper.Component.call(this, /*...any args required by it...*/);
// Button stuff here...
};
Function#call calls a function with a specific this value and any arguments you pass it. So the above calls Paper.Component from within Paper.Button with this referencing the current object, and passing along any appropriate arguments.
You also want to set the constructor property on any prototype you replace rather than just adding to. It's largely optional (JavaScript itself doesn't use constructor for anything), but since the JavaScript engine sets it on the default prototype objects, we should set it when replacing them, just so we're consistent with the default prototypes.
Slightly simpler, concrete example:
function Shape(sides, color) {
this.sides = sides;
this.color = color;
}
// Shape.prototype.method = ...
function Rectangle(color) {
Shape.call(this, 4, color);
// Rectangle stuff here...
}
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle; // <== The constructor bit
// Rectangle.prototype.method = ...
If you're interested in setting up hierarchies of "classes" of objects using prototypical inheritance in JavaScript, you might want to look at my Lineage helper script, which automates the above with a simpler syntax and provides other useful features.
A good reference is the MDN - Inheritance revisited page
What you are looking for (I think) is something like this:
Paper.Component = function(x,y,w,h){
this.setPosition( x, y );
this.setSize( w, h );
};
Paper.Component.prototype.isInBounds = function(x, y){};
Paper.Component.prototype.setPosition = function(x, y){};
Paper.Component.prototype.setSize = function(w, h){};
Paper.Button = function(x, y, w, h, text){
Paper.Component.apply( this, arguments );
}
Paper.Button.prototype = Object.create(Paper.Component.prototype);
Paper.Button.prototype.constructor = Paper.Button;
Things to note:
Do not do Paper.Component.prototype = { ... } as it will overwrite the current prototype (e.g. the existing .prototype.constructor and anything else anyone has already created).
Remember to set the constructor (the last line).
[Edited: vector2d and vector3d are not good examples. I am now using pt and ptMass instead.]
I have been searching answers for this but there seems no good solution available.
Suppose I have an object as below.
function pt(x,y){
this.x = x;
this.y = y;
}
Now, I would like to have a point mass as below.
function ptMass(x,y,m){
this.x = x;
this.y = y;
this.m = m;
}
It is better to use inheritance to create the ptMass class.
I am currently using the following way to do it.
function ptMass(x,y,m){
pt.apply(this.arguments);
this.m = m;
}
Is there a way of doing this with prototype?
I have tried the following but it doesn't work.
pt = function(m){this.m = m};
ptMass.prototype = new pt();
One more question, what is the advance of using prototypical inheritance?
First, why is it better to inherit it?
I get several arguments for inheritance, though I prefer composition (modules/components and dependency-injection) myself.
Second, you get reuse out of doing:
function Vector2D (x, y) {
this.x = x;
this.y = y;
}
function Vector3D (x, y, z) {
Vector2D.call(this, x, y);
this.z = z;
}
Third, in a large-scale JS app, creating tens of thousands (or millions) of polygons, the extra function calls are just going to slow it down, unnecessarily.
Fourth, you can't use
Vector3D.prototype = new Vector2D();
First, you're not initializing x and y to anything inside of the Vector2D.
Second, prototype is meant to hold STATIC properties and functions, if you're coming from other languages.
Even if you did initialize Vector2D, it would still be the exact same Vector2D for every instance of your Vector3D class.
Why not just:
var point2D = function (x, y) {
return { x : x,
y : y };
},
point3D = function (x, y, z) {
return { x : x,
y : y,
z : z };
};
...and when you have those particular building-blocks as your base elements, compose different modules which use them?
EDIT
function anObj (x, y) {
var privateFunc = function () { return y; };
return {
x : x,
method : function () { return privateFunc(); }
};
}
var myObj = anObj(1, 2);
myObj.x; // 1
myObj.method(); // 2
The upside here is that you've now got private variables (privateFunc and y).
The downside is that in terms of memory-usage, each instance of your object needs to have its OWN copy of any private methods.
So if you're making hundreds of thousands of these objects (vertices/polygons, for example), and they don't NEED to have private state, this isn't the method to use.
If, however, you're making players or enemies, or controllers, or anything which you DON'T want tampered with, then you DO want to use a method like this (or more advanced).
If you have a situation where you might want STATIC data / methods which are also 100% private, then you should try a format like this:
var objectMaker = (function () {
var staticData = 32,
staticFunc = function (num) { return num + staticData; };
return function (x, y) {
var privateData = 12,
privateMethod = function (num) {
return y + privateData + staticFunc(num);
};
return { x : x,
method : function (num) { return privateMethod(num); }
};
};
}());
var myObj = objectMaker(3, 4);
myObj.x; // 3;
myObj.method(12); // 12 + y(y === 4) + privateData + staticData;
So what we've done here is we've got an immediately-firing function (it fires as soon as its defined, and returns the value of the function to the variable).
So in our particular case, this function immediately returns the actual function that we want to use to make new instances.
The private variables and functions that are inside of the immediate function (not the constructor we return) are static, in that every instance you create will have access to the exact same functions/data inside of that closure.
The downside is that those static functions have NO access to private (or even instance-specific) data.
This means that you have to pass your values into the static function, and catch the return from the static function, as you can't rely on the static methods to modify instance values (technically, you can modify objects/arrays, directly if you pass them into the function, but otherwise you must catch return values).
Now you can have plenty of helper functions which are shared amongst instances (lower memory), while still being private and secure.
If what you want is public properties, public methods, and static methods/properties, THEN you can access them like so:
var Obj = function (x, y) {
this.x = x;
this.per_instance_method = function () { return y; };
};
Obj.prototype.staticData = { z : 32 };
Obj.prototype.staticMethod = function () { return this.x + this.staticData.z; };
var myObj = new Obj(3, 4);
myObj.staticMethod();
...just don't expect any prototype method to have access to any instance's private data (like y).
No, your first one was nearly correct:
function vector3d(x,y,z){
vector2d.apply(this, arguments);
// you might also use vector2d.apply(this, [].slice.call(arguments, 0,2));
// or simpler vector2d.call(this, x, y);
this.z=z;
}
So yes, you must call it from the constructor. Calling it once to create a prototype object would set up inheritance correctly, but create an instance of vector2D - which you neither need nor want. It might even cause harm. Have a look at the detailed answers on What is the reason [not] to use the 'new' keyword [for inheritance]?.
Is there a way of doing this with prototype?
vector3d.prototype = Object.create(vector2d.prototype);
will make the prototype object, from which all instances of vector3 inherit, inherit from the prototype object of vector2d. I'm not sure whether you need that, none of the usual twodimensional methods would apply on a threedimensional vector.
what is the advance of using prototypical inheritance?
Benefits of prototypal inheritance over classical?
What does it mean that Javascript is a prototype based language?