Constructor Based Inheritance in JavaScript - javascript

I have the following code:
function Vector(X,Y) //Constructor
{
this.X = X;
this.Y = Y;
}
function Box(Size /*Vector*/, Position /*Vector*/) //Constructor
{
this.Size = Size;
this.Position = Position;
this.Velocity = new Vector(0,0);
this.Anchored = true;
this.CanCollide = false;
this.Colour = "rgb(50,50,50)";
this.draw = function()
{
ctx.fillStyle = this.Colour;
ctx.fillRect(this.Position.X-(this.Size.X*0.5),this.Position.Y-(this.Size.Y*0.5),this.Size.X,this.Size.Y);
}
}
function Player(Size,Position)
{
Box(Size,Position);
this.Anchored = false;
this.CanCollide = true;
this.Colour = "rgb(0,100,0)";
}
var Me = new Player(new Vector(25,25), new Vector(10,10));
console.log(Me.Velocity);
If you look at the first statement in the constructor function, 'Player', you'll see that I called the function Box; I'm trying to inherit the properties and methods of 'Box' into 'Player'. I don't get any errors, however when I try and reference an inherited property, (the last statement), it returns undefined.
Why doesn't Player inherit Box's properties? I understand that JS is prototype based, and that this is extremely unorthodox, but I cannot make any sense as to how to inherit through multiple objects using prototypes.

It will not inherit the properties of Box that way. That is because you are calling the Box function on the global context and the value of this will point to the global object window. To change the value of the this inside the function, use call() or apply() or even bind().
When you change the value of this this way inside the Player function, the initialisation code inside Box will be run with the instance of Player as its context.
function Player(Size,Position)
{
Box.call(this,Size,Position); //the this value will point to instance of Player
}

Another options is to set Player's prototype:
Player.prototype = new Box();
Player.prototype.constructor = Player;
Then you wouldn't call the Box constructor directly. It would be called for you, so:
function Player(Size,Position)
{
this.Size = Size;
this.Position = Position;
this.Anchored = false;
this.CanCollide = true;
this.Colour = "rgb(0,100,0)";
}

Related

Is there a way to use a function parameter as a getter in a prototype?

Im trying to code pacman at the moment, and a came across a problem:
Since all the Ghosts use the same pathfinding and are generally pretty simular, I want to use a prototype for them. The only property in which they really differ, is the way, they choose their target position. I'd like to give a function to the prototype and use it as a getter. Is that possible?
function Ghost(color,x,y,getterFunction){
this.color = color;
this.x = x;
this.y = y;
this.direction = "up";
this.move = function(){
//Pathfind towards this.target
}
this.target = getterFunction; //or something like this...
}
Thanks for your help :^)
#Bergi is right. You don't want to use it as a getter. If you tried to add it to the prototype it would be overwritten by every new ghost you create, since the prototype is a shared object.
The prototype is for shared functionality. Instance functionality belongs in the instance, i.e. In your constructor function.
Your move function should be on the prototype. But target should be an instance method. You could set a default method for target on the prototype. Any instance method would be called before looking to the prototype.
Example
function Ghost(color, x, y, target){
// everything in here is instance specific
this.color = color;
this.x = x;
this.y = y;
this.direction = "up";
if (typeof target === 'function') {
this.target = target;
}
}
// this is the prototype
Ghost.prototype.move = function() {
//Pathfind towards this.target
this.target()
}
Ghost.prototype.target = function() {
// default target to use if no target provided at creation
}
So now, when you do this:
var redGhost = new Ghost('red', 0, 0, function() {
//do something unique
})
You'll have a ghost that's red and has a custom target function. But if you do this:
var yellowGhost = new Ghost('yellow', 0, 0)
You'll have a ghost that uses the default target function you added to the prototype.

How to initialize a subclass's prototype property [duplicate]

I don't understand this behavior in javascript for inheritance I've always seen it defined like so :
function GameObject(oImg, x, y) {
this.x = x;
this.y = y;
this.img = oImg;
this.hit = new Object();
this.hitBox.x = x;
this.hitBox.y = y;
this.hitBox.width = oImg.width;
this.hitBox.height = oImg.height;
}
Spaceship.prototype = new GameObject();
Spaceship.prototype.constructor = Spaceship;
function Spaceship(){
console.log("instantiate ship");
GameObject.apply(this, arguments);
this.vx = 0;
this.vy = 0;
this.speed = 3;
this.friction = 0.94;
}
But in my case, these lines :
this.hitBox.width = oImg.width;
this.hitBox.height = oImg.height;
When I do a console.log(this) in my Spaceship constructor, I can see that the proto property is set to Spaceship instead of GameObject, if I remove them, it is set to GameObject.
And if I use :
Spaceship.prototype = GameObject.prototype;
I have no more problems with that. The reason that this blocks me is that I have another object with an add() method and it checks that the object inerhits of GameObject with this code :
if(object instanceof GameObject)
I don't understand what those two lines can probably change so that inheritance is broken when they are present and I'm not sure doing inheritance the second way is good. Could someone enlighten me about this please ? :)
If you do
Spaceship.prototype = GameObject.prototype;
Then they both refer to the same object, so you might as well have everything in GameObject, if you add something to Spaceship.prototype, it will be added to GameObject.prototype as well. You can easily test it by adding something to Spaceship.prototype after the assignment. For example, in your case you can see that GameObject.prototype.constructor is actually Spaceship.
As for
Spaceship.prototype = new GameObject();
This invokes the constructor which might have undesired side effects, you rather want to use:
Spaceship.prototype = Object.create(GameObject.prototype);
Where the used Object.create functionality here comes down to:
Object.create = function( proto ) {
function f(){}
f.prototype = proto;
return new f;
};
Modern browsers already have the function though.
It was never properly explained why you were getting weird behavior with this.hitBox (I think that's what you were trying to say).
If you do inheritance by invoking the parent's constructor to create a prototype, that parent's constructor is executed once to create an instance of the parent type, and then all instances of the child type will share that one instance as their prototype.
The problem with this is that if that constructor has any lines that assign mutable objects to this, then those objects will be properties on that prototype and any modifications to those objects will be reflected across all instances of the child type:
Spaceship.prototype = new GameObject();
Spaceship.prototype.constructor = Spaceship;
var sps1 = new Spaceship();
var sps2 = new Spaceship();
sps1.hitBox.x = 9;
sps2.hitBox.x = 12;
console.log(sps1.hitBox.x); // 12 (oh noes! what happened)
console.log(sps2.hitBox.x); // 12
(there are other, similar problems with the "call a constructor to make a prototype" approach, but I'll just leave it here on that point)
#Esailija's suggestion to use Object.create(baseObject) is the first step to solving this problem. It creates a new object whose prototype is baseObject, but without the stuff that is set up in the constructor (This is a good thing, but it needs to be accounted for. Read on...).
As I just said, this will create an object where the initialization logic in the parent's constructor has never run, but in most cases that logic is relevant to the object's functionality. So there is one more thing you need to do, which is to have the child constructor call the parent constructor:
function Spaceship(oImg, x, y) {
// call parent constructor on this object and pass in arguments.
// you could also use default values for the arguments when applicable
GameObject.call(this, oImg, x, y);
// remainder of Spaceship constructor...
}
This will ensure that the parent constructor logic runs separately for every new Spaceship, and carries out the necessary initialization tasks.
function GameObject(oImg, x, y) {
this.x = x;
this.y = y;
this.img = oImg || {width:null, height: null};
this.hitBox = new Object();
this.hitBox.x = x;
this.hitBox.y = y;
this.hitBox.width = this.img.width;
this.hitBox.height = this.img.height;
}
function Spaceship(){
GameObject.apply(this, arguments);
this.vx = 0;
this.vy = 0;
this.speed = 3;
this.friction = 0.94;
}
Spaceship.prototype = new GameObject();
var sps1 = new Spaceship();
var sps2 = new Spaceship();
sps1.hitBox.x = 9;
sps2.hitBox.x = 12;
console.log(sps1.hitBox.x); // 9
console.log(sps2.hitBox.x); // 12

JS - Why does there have to be an intermediary when inheriting? [duplicate]

I don't understand this behavior in javascript for inheritance I've always seen it defined like so :
function GameObject(oImg, x, y) {
this.x = x;
this.y = y;
this.img = oImg;
this.hit = new Object();
this.hitBox.x = x;
this.hitBox.y = y;
this.hitBox.width = oImg.width;
this.hitBox.height = oImg.height;
}
Spaceship.prototype = new GameObject();
Spaceship.prototype.constructor = Spaceship;
function Spaceship(){
console.log("instantiate ship");
GameObject.apply(this, arguments);
this.vx = 0;
this.vy = 0;
this.speed = 3;
this.friction = 0.94;
}
But in my case, these lines :
this.hitBox.width = oImg.width;
this.hitBox.height = oImg.height;
When I do a console.log(this) in my Spaceship constructor, I can see that the proto property is set to Spaceship instead of GameObject, if I remove them, it is set to GameObject.
And if I use :
Spaceship.prototype = GameObject.prototype;
I have no more problems with that. The reason that this blocks me is that I have another object with an add() method and it checks that the object inerhits of GameObject with this code :
if(object instanceof GameObject)
I don't understand what those two lines can probably change so that inheritance is broken when they are present and I'm not sure doing inheritance the second way is good. Could someone enlighten me about this please ? :)
If you do
Spaceship.prototype = GameObject.prototype;
Then they both refer to the same object, so you might as well have everything in GameObject, if you add something to Spaceship.prototype, it will be added to GameObject.prototype as well. You can easily test it by adding something to Spaceship.prototype after the assignment. For example, in your case you can see that GameObject.prototype.constructor is actually Spaceship.
As for
Spaceship.prototype = new GameObject();
This invokes the constructor which might have undesired side effects, you rather want to use:
Spaceship.prototype = Object.create(GameObject.prototype);
Where the used Object.create functionality here comes down to:
Object.create = function( proto ) {
function f(){}
f.prototype = proto;
return new f;
};
Modern browsers already have the function though.
It was never properly explained why you were getting weird behavior with this.hitBox (I think that's what you were trying to say).
If you do inheritance by invoking the parent's constructor to create a prototype, that parent's constructor is executed once to create an instance of the parent type, and then all instances of the child type will share that one instance as their prototype.
The problem with this is that if that constructor has any lines that assign mutable objects to this, then those objects will be properties on that prototype and any modifications to those objects will be reflected across all instances of the child type:
Spaceship.prototype = new GameObject();
Spaceship.prototype.constructor = Spaceship;
var sps1 = new Spaceship();
var sps2 = new Spaceship();
sps1.hitBox.x = 9;
sps2.hitBox.x = 12;
console.log(sps1.hitBox.x); // 12 (oh noes! what happened)
console.log(sps2.hitBox.x); // 12
(there are other, similar problems with the "call a constructor to make a prototype" approach, but I'll just leave it here on that point)
#Esailija's suggestion to use Object.create(baseObject) is the first step to solving this problem. It creates a new object whose prototype is baseObject, but without the stuff that is set up in the constructor (This is a good thing, but it needs to be accounted for. Read on...).
As I just said, this will create an object where the initialization logic in the parent's constructor has never run, but in most cases that logic is relevant to the object's functionality. So there is one more thing you need to do, which is to have the child constructor call the parent constructor:
function Spaceship(oImg, x, y) {
// call parent constructor on this object and pass in arguments.
// you could also use default values for the arguments when applicable
GameObject.call(this, oImg, x, y);
// remainder of Spaceship constructor...
}
This will ensure that the parent constructor logic runs separately for every new Spaceship, and carries out the necessary initialization tasks.
function GameObject(oImg, x, y) {
this.x = x;
this.y = y;
this.img = oImg || {width:null, height: null};
this.hitBox = new Object();
this.hitBox.x = x;
this.hitBox.y = y;
this.hitBox.width = this.img.width;
this.hitBox.height = this.img.height;
}
function Spaceship(){
GameObject.apply(this, arguments);
this.vx = 0;
this.vy = 0;
this.speed = 3;
this.friction = 0.94;
}
Spaceship.prototype = new GameObject();
var sps1 = new Spaceship();
var sps2 = new Spaceship();
sps1.hitBox.x = 9;
sps2.hitBox.x = 12;
console.log(sps1.hitBox.x); // 9
console.log(sps2.hitBox.x); // 12

How to inherit an object in this case?

This has probably been asked in some form and I tried to check this resource: https://developer.mozilla.org/en/docs/Web/JavaScript/Inheritance_and_the_prototype_chain
My gameobject.js looks like this:
function GameObject(program)
{
this.program = program;
this.graphics = null;
}
GameObject.prototype.update = function(dt)
{
}
GameObject.prototype.draw = function()
{
// Here graphics would be the graphics of the child class,
// because the graphics would be replaced with the child class' graphics
if(this.graphics !== null)
this.program.renderer.render(this.graphics);
}
And I want to have another class called, for example, box.js:
function Box(program, width, height, x, y)
{
// Call base constructor here?
this.width = width;
this.height = height;
this.x = x;
this.y = y;
}
And Now I want to basically inherit the update and draw methods from GameObject as well as call the GameObject constructor with the program parameter, so that in the end the thing should work like this:
var box = new Box(program, 10, 10, 100, 100);
// In game class update method
box.update(this.td);
// In game class draw method
box.draw();
So basically like how it would be done in C#. It would already help a lot, if only I could get the Box to inherit the update and draw methods from the GameObject.
Edit 1: Jsfiddle here: https://jsfiddle.net/0df9rfu5/1/
Edit 2: I tried a workaround like this:
function Box(program, width, height, x, y)
{
var self = this;
this.base = new GameObject(program);
this.width = width;
this.height = height;
this.x = x;
this.y = y;
this.update = function(dt) { self.base.update(dt); };
this.draw = this.base.draw();
}
So I would be creating a new instance of the base class GameObject every time a Box is created and then I set the box' update and draw methods to those of GameObject.
This isn't doing the trick though and I think there is something deeply wrong with this method any way.
Edit 3: Maybe I should just do this like I've always done... everything that inherits from GameObject still has to override the update and draw methods. It is just that I guess I can't be sure that every object in gameobject list has draw and update methods and I will just have to assume they do.
Constructors in JavaScript are just plain functions. To call GameObject from inside the Box constructor you would use:
GameObject.call(this, program);
To have Box objects inherit form GameObject use
Object.setPrototypeOf(Box.prototype, GameObject.prototype);
Be sure to put the line above before the first invocation of new Box.
For best compatibility with older browsers (tanks #Bergi), you can overwrite Box.prototype with a new object instead of using Object.setPrototypeOf.
Box.prototype = Object.create(GameObject.prototype, { configurable: true, writable: true, value: Box });
Then you should be able to use the methods draw and update on Box objects.
The example below should get you on the right track
function GameObject(program)
{
this.program = program;
this.graphics = null;
}
GameObject.prototype.update = function(dt)
{
alert('updating');
}
GameObject.prototype.draw = function()
{
// Here graphics would be the graphics of the child class,
// because the graphics would be replaced with the child class' graphics
if(this.graphics !== null)
this.program.renderer.render(this.graphics);
}
function Box(program, width, height, x, y)
{
GameObject.call(this, program);
this.width = width;
this.height = height;
this.x = x;
this.y = y;
}
Object.setPrototypeOf(Box.prototype, GameObject.prototype);
let b = new Box();
b.update();

how can i inherit from 2 objects and use both the objects as a prototype of another

I am learning js inheritance and prototyping and my naming is probably totally off i am sorry for that.
i am trying to create a super object and prototype 2 sub object as properties and then inside one of the sub object call a function that is found in the other. it doesn't work for some reason.
UPDATE
my goal in here is:
I am trying to make a small game - for fun and practice.
My plan was to have 1 basic object called(object) that has positioning and other properties (that every other object will have) another object called(controls) for controls. Only objects that can move will have that object as well.
players are also objects and they will have both "object" and "controls". as their prototype.
Hope that cleared things a bit.
Code:
// sub Object1
function object(){
this.speed = 1;
this.walkDistant = 5;
}
// sub Object2
function controls(){
this.moveLeft = function(){
console.log(this.speed , this.walkDistant);
return this.speed * this.walkDistant;
}
}
// super Object
function player(){
// DoesNothing
}
player.prototype.object = new object();
player.prototype.controls = new controls();
var firstPlayer = new player();
console.log(firstPlayer.controls.moveLeft());
Or if you prefer fiddle : http://jsfiddle.net/rMaKa/1/
Because a Player can be controlled you can mix in Controls with Player. Your object constructor function is a badly chosen name because a constructor function should start with a capital making it Object and you'd overwrite window.Object (bad idea). For this reason I've renamed it to Base. Player is a Base object and can be controlled so inherits from Base and has Controls mixed in.
For more information about constructor functions, mix ins, instance members and prototype check this link.
function Base() {
this.speed = 1;
this.walkDistant = 5;
}
// sub Object2
function Controls() {
}
Controls.prototype.moveLeft = function() {
console.log(this.speed, this.walkDistant);
return this.speed * this.walkDistant;
}
// super Object
function Player() {
//make player have Base instance members
Base.call(this);
//make player heve Controls instance members
Controls.call(this);
}
//player is a base object
Player.prototype = Object.create(Base.prototype);
//repair constrictor
Player.prototype.constructor = Player;
//Player can be controlled, copy controls prototype on player (mixin)
// this would be better suited in a helper function, see link posted in answer
var stuff;
for (stuff in Controls.prototype) {
if (Controls.prototype.hasOwnProperty(stuff)) {
Player.prototype[stuff] = Controls.prototype[stuff];
}
}
var firstPlayer = new Player();
console.log(firstPlayer.moveLeft());
If you want the player to have controls you can try something like this:
function Controls(what) {
//what do we need to control
this.controlWhat=what;
}
Controls.prototype.moveLeft = function() {
console.log(this.controlWhat.speed, this.controlWhat.walkDistant);
return this.controlWhat.speed * this.controlWhat.walkDistant;
};
function Player() {
this.speed = 1;
this.walkDistant = 5;
this.controls=new Controls(this);
}
var firstPlayer = new Player();
console.log(firstPlayer.controls.moveLeft());
The problem is that you are trying to access a property that pertences to subObj1 from subObj2, but is the superObj that inherit both.
To achieve that, you should make your subObj1 inherit the subObj2.
// sub Object1
function name(){
this.name = function(){
var myName = 'FirstName';
console.log(myName, this.last.lastName);
}
this.callName = function(){
this.name();
};
}
// sub Object2
function lastName(){
this.lastName ='someLastName';
}
// super Object
function fullName(){
// DoesNothing
}
name.prototype.last = new lastName();
fullName.prototype.name = new name();
var myName = new fullName();
myName.name.callName();
You can see this fiddle
You can use Mixins to extend the functionality of an object using functionality already implemented in other objects. You could also have it that the sub-objects know about the super object as below.
function subClassA(containerClass) {
this.containerClass = containerClass;
this.methodA = function() {
console.log(this.containerClass.b.methodB());
}
}
function subClassB(containerClass) {
this.containerClass = containerClass;
this.methodB = function() {
return 12345;
}
}
function containerClass() {
this.a = new subClassA(this);
this.b = new subClassB(this);
}
var cc = new containerClass();
cc.a.methodA();
The Mixin approach would look something like this:
// extend function to add mixin support
function extend(destination, source) {
for (var k in source)
if (source.hasOwnProperty(k))
destination[k] = source[k];
return destination;
}
function subClassA() { }
subClassA.prototype.methodA = function() {
console.log(this.methodB());
};
function subClassB() { }
subClassB.prototype.methodB = function() {
return 12345;
};
function superClass() {
// ----------------
}
// add the subClassA and subClassB functionality
extend(superClass.prototype, subClassA.prototype);
extend(superClass.prototype, subClassB.prototype);
var sc = new superClass();
sc.methodA();

Categories

Resources