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

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

Related

Diffrence between assigning simple prototype and using Object.Create(Object.prototype) in protypal inheritence in JavaScript?

I am implementing Prototypal inheritence in java script using simple assigning of prototype this is my code -
var Rectangle = function(heigth, width) {
this.height = height;
this.width = width;
}
Rectangle.prototype.area = function() {
return this.height*this.width;
}
var Square = function(length) {
this.height = this.width = length;
}
Square.prototype = Rectangle.prototype;
var sqr1 = new Square(5);
console.log(sqr1.area());
But at the place of Square.prototype = Rectangle.prototype it is recommended to use Square.prototype = Object.create(Rectangle.prototype). Can anyone tell me the underlying difference because the above code is working fine.
Because the statement Square.prototype = Rectangle.prototype; actually copies the reference of the Rectangle.prototype and assigns it to Square.prototype. (Primitive types are copied by value and reference types are copied by reference).
So, if you are going to add some properties and methods on Square.prototype it'll modify the original Rectangle.prototype object and which is not what you want.
The Square.prototype = Object.create(Rectangle.prototype) creates a new object whose prototype is the Rectangle.prototype which is correct way.

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

Constructor Based Inheritance in 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)";
}

Javascript object design

In Javascript I would like to create two classes: A node, and a node list. A node contains some trivial properties; a node list contains pointers to a node, and multiple node lists can contain the same nodes. Would the following be correct (simplistic) design?
function Node(name, x, y) {
this.name = name;
this.x = x;
this.y = y;
}
Node.prototype.setX = function(x) {
this.x = x;
};
Node.prototype.setY = function(y) {
this.y = y;
};
function Nodelist() {
this.list = [];
}
Nodelist.prototype.addNode = function(node) {
this.list.push(node);
};
var a = new Node('stack', 0, 0);
var b = new Node('overflow', 0, 0);
var l = new Nodelist();
var m = new Nodelist();
l.addNode(a);
l.addNode(b);
m.addNode(a);
Do I even need these .prototype.set functions? Playing around in the console it seems I can just do a node.x = 10. Thanks.
not sure what your intention is (setters with no getters?), but you might be interested in private variables. to achieve the effect of private variables, you would start with the following:
function Guy(name) {
var _name = name;
this.getName = function(){ return _name; }
this.setName = function(n) { _name = n; }
}
var g = new Guy("Bob");
alert(g.getName()); // works
alert(g._name); // doesn't work
(In fact in this simple example, you don't even need the variable _name; getName and setName can close over the function argument name);
No, you don't need those functions, unless you need some sort of callback-based system where a function should be executed when the value changes. You can access and assign to the properties directly, as you discovered.
Javascript objects properties are accessible from anywhere ie. there are no real private variables so defining getter setter methods in this way is kind of pointless. If you want private variables or similar behaviour, read this http://javascript.crockford.com/private.html
It depends on if you're trying to enforce the encapsulation of x and y in an OOP manner. One way that javascript differs from - for example - Java is that it doesn't inherently enforce private variables. Usually, the common way to declare that some variable/method SHOULD be private is to name it with an underscore. So if you're actually trying to enforce OOP concepts here, then declare x and y like this:
function Node(name, x, y) {
this.name = name;
this._x = x;
this._y = y;
}
And then keep your setters. If you aren't trying to enforce some kind of encapsulation of x and y to your Node, then go ahead and don't provide them and just use the node.x/node.y when you need to get/set x or y.
Just keep in mind that this is simply a naming convention and when this script is running, _x is just as visible as x. It will be up to you and any programmers you work with to enforce this.

Categories

Resources