I'm a newbie to javascript and I need some help.
I was trying to sum radius by function, but got an undefined error:(
function sumWithFunction(func, number) {
return func() + number;
}
function Circle(X, Y, R) {
this.x = X;
this.y = Y;
this.r = R;
}
Circle.prototype.getRadius = function () {
return this.r;
}
Circle.prototype.increaseRadiusBy = function(number) {
this.r = sumWithFunction(this.getRadius, number);
}
function addFivetoIt(func) {
func(5);
}
var MyCircle = new Circle(0, 0, 10);
addFivetoIt(MyCircle.increaseRadiusBy);
The problem is that you're passing a function a reference to another function, and the passed function is therefore losing scope! Here's the offending line:
Circle.prototype.increaseRadiusBy = function(number) {
this.r = sumWithFunction(this.getRadius, number);
}
JavaScript objects are in some ways simpler than they appear. When you added the getRadius method to the Circle prototype, you were not defining a class method like you would in classical OO. You were simply defining a named property of the prototype, and assigning a function to the value of that property. When you pass this.getRadius as an argument to a static function, like sumWithFunction, the context of this is lost. It executes with the this keyword bound to window, and since window has no r property, the browser throws an undefined error.
Put another way, the statement this.getRadius() is actually saying "execute the function assigned to the getRadius property of this, and execute it in the context of this. Without calling the function explicitly through that statement, the context is not assigned.
A common solution to this is to add an expected argument to any function which receives another function, for context.
function sumWithFunction(func, context, number) {
return func.apply(context) + number;
}
function Circle(X, Y, R) {
this.x = X;
this.y = Y;
this.r = R;
}
Circle.prototype.getRadius = function () {
return this.r;
}
Circle.prototype.increaseRadiusBy = function(number) {
this.r = sumWithFunction(this.getRadius, this, number);
}
function addFivetoIt(func, context) {
func.apply(context,[5]);
}
var MyCircle = new Circle(0, 0, 10);
addFivetoIt(MyCircle.increaseRadiusBy, myCircle);
A simpler, but less robust solution would be to declare a function inline that can access a context reference in the local closure.
function sumWithFunction(func, number) {
return func() + number;
}
function Circle(X, Y, R) {
this.x = X;
this.y = Y;
this.r = R;
}
Circle.prototype.getRadius = function () {
return this.r;
}
Circle.prototype.increaseRadiusBy = function(number) {
var me = this;
this.r = sumWithFunction(function() {
return me.getRadius()
}, number);
}
function addFivetoIt(func) {
func(5);
}
var MyCircle = new Circle(0, 0, 10);
addFivetoIt(function(number) {
return MyCircle.increaseRadiusBy(number);
});
But by far the simplest solution is to use a newer feature of ECMAScript, a function method called bind. It is explained well here, including the fact that it is not supported by all browsers. That's why a lot of libraries, like jQuery, Prototype, etc., have cross-browser function-binding utility methods like $.proxy.
function sumWithFunction(func, number) {
return func() + number;
}
function Circle(X, Y, R) {
this.x = X;
this.y = Y;
this.r = R;
}
Circle.prototype.getRadius = function () {
return this.r;
}
Circle.prototype.increaseRadiusBy = function(number) {
this.r = sumWithFunction(this.getRadius.bind(this), number); // or $.proxy(this.getRadius,this)
}
function addFivetoIt(func) {
func(5);
}
var MyCircle = new Circle(0, 0, 10);
addFivetoIt(MyCircle.increaseRadiusBy.bind(MyCircle)); // or $.proxy(MyCircle.increaseRadiusBy,MyCircle)
The tricky thing with this in JavaScript is that it contains the object the function was a property of when it was called by default. So when you pass MyCircle.increaseRadiusBy as a parameter, you then call it as func(), so the function isn't a property of any object. The easiest way to set this is to use the call() function:
function addFivetoIt(func, context) {
// The first parameter to `call()` is the value of `this` in the function
func.call(context, 5);
}
var MyCircle = new Circle(0, 0, 10);
addFivetoIt(MyCircle.increaseRadiusBy, MyCircle);
The below approach, setting func as a property before calling it, also works. You would never do this in practice because it adds an unnecessary property to context, but it's a good didactic example to show how this works.
function addFivetoIt(func, context) {
context.func = func;
context.func(5);
}
var MyCircle = new Circle(0, 0, 10);
addFivetoIt(MyCircle.increaseRadiusBy, MyCircle);
Related
I write the Javascript code as follow:
function Shape() {
this.x = 0;
this.y = 0;
}
function Rectangle() {
Shape.call(this); // call super constructor.
}
Rectangle.prototype = Object.create(Shape.prototype);
function Tmp() {
this.a = 0;
this.b = 0;
}
Rectangle.prototype.constructor = Tmp;
var rect = Object.create(Rectangle.prototype);
console.log(rect)
then the output is:
The rect should be initialized by constructor function Tmp.my question is where is the attribution a and b of the object rect which initialized by the constructor function Tmp?
The rect should be initialized by constructor function Tmp
If you want that, you'd do:
Tmp.call(rect);
...after
var rect = Object.create(Rectangle.prototype);
function Shape() {
this.x = 0;
this.y = 0;
}
function Rectangle() {
Shape.call(this); // call super constructor.
}
Rectangle.prototype = Object.create(Shape.prototype);
function Tmp() {
this.a = 0;
this.b = 0;
}
//Rectangle.prototype.constructor = Tmp;
var rect = Object.create(Rectangle.prototype);
Tmp.call(rect);
console.log(rect);
Object.create doesn't call any functions at all, it just creates the object with the given prototype. So neither Rectangle nor Shape nor Tmp is called by doing var rect = Object.create(Rectangle.prototype);.
Or if you also want Rectangle to do its job, replace the Object.create call with:
var rect = new Rectangle();
Tmp.call(rect);
It's very strange to set the constructor property of Rectangle.prototype to Tmp, not Rectangle. I would strongly suggest not doing that. And there's no need to if all you want is for Tmp to initialize an instance. The constructor property of the object referenced by SomeFunction.prototype should be SomeFunction, never anything else.
I was really struggling coming up with a title but basically I'm working on a game in the html5 canvas and have a class called player with a subclass aiPlayer for when playing against ai. The code for updating the players looks like this:
var entitiesCount = this.entities.length;
for (var i = 0; i < entitiesCount; i++) {
var entity = this.entities[i];
entity.update();
if (entity instanceof special && entity.x > WIDTH || entity.x + 200 < 0) {
this.entities.splice(i, 1);
entitiesCount--;
}
}
However, the aiPlayer never updates with the aiPlayer update function. I've printed out the constructor of each entity and there is one Player and one aiPlayer. However, when printing out the method they are calling, both of them are calling the Player update. Does anyone know why it would do this?
Also, if it helps, the aiPlayer update looks like:
aiPlayer.prototype.update = function() {
if((this.game.timer.gameTime % this.moveTime) > (this.moveTime * 0.9)) {
this.chooseMove();
}
Player.prototype.update.call(this);
};
And ai constructor looks like:
function aiPlayer (game, character, x, y, health) {
Player.call(this, game, character, x, y, health, PLAYER2_CONTROLS, "left");
aiPlayer.prototype = new Player(this.game, this.character, this.x, this.y,
this.health, this.control, this.facing);
aiPlayer.prototype.constructor = aiPlayer;
this.controls = PLAYER2_CONTROLS;
this.attackLength = 50;
this.fleeLength = 70;
this.moveTime = 1;
this.prevControl = "idle";
}
function aiPlayer (game, character, x, y, health) {
Player.call(this, game, character, x, y, health, PLAYER2_CONTROLS, "left");
aiPlayer.prototype = new Player(this.game, this.character, this.x, this.y,this.health, this.control, this.facing);
aiPlayer.prototype.constructor = aiPlayer;
this.controls = PLAYER2_CONTROLS;
this.attackLength = 50;
this.fleeLength = 70;
this.moveTime = 1;
this.prevControl = "idle";
}
These lines here
aiPlayer.prototype = new Player(this.game, this.character,
this.x, this.y,this.health,
this.control, this.facing);
aiPlayer.prototype.constructor = aiPlayer;
are wrong. They are wrong because
you are setting the prototype to an Instance of Player
you are resetting the prototype and the constructor of the prototype of aiPlayer every time you create a new instance of aiPlayer. You should move all modifications to the prototype outside of the constructor function, like this:
-
function aiPlayer (game, character, x, y, health) {
Player.call(this, game, character, x, y, health, PLAYER2_CONTROLS, "left");
this.controls = PLAYER2_CONTROLS;
this.attackLength = 50;
this.fleeLength = 70;
this.moveTime = 1;
this.prevControl = "idle";
}
aiPlayer.prototype.someMethod = function someMethod() {
....
}
A correct way to set the prototype of the subclass is like this
aiPlayer.prototype = Object.create(Player.prototype, {
constructor : {
value : aiPlayer
}
};
This will set as the aiPlayer prototype a new object that inherits from Player.prototype (i.e. has Player.prototype as its prototype) and has aiPlayer registered as its constructor function
Also, the .update of Player is called from aiPlayer because you explicitly calling it here
aiPlayer.prototype.update = function() {
if((this.game.timer.gameTime % this.moveTime) > (this.moveTime * 0.9)) {
this.chooseMove();
}
Player.prototype.update.call(this); //you call the Player.update()
};
Considering the above, this is how you should register the aiPlayer.update
aiPlayer.prototype = Object.create(Player.prototype, {
constructor : {
value : aiPlayer
}
};
aiPlayer.prototype.update = function update() {
//your code here
}
Now, when you create a new aiPlayer object instance, the inheritance chain will go like this
aiPlayerInstance --> aiPlayer.prototype --> Player.prototype
and when you call aiPlayerInstance.update() it will first look to aiPlayer.prototype and since aiPlayer.prototype does have a method called update it will execute it, and it will not look any further down the inheritance chain (i.e. in Player.prototype)
So I'm making my "Sprite" class, and right now it works properly when it's laid out like this (alot of this is unnecessary, but might help you understand):
function Entity(tname)
{
if (typeof (tname) === 'undefined') tname = "Entity";
this.tname = tname;
}
Entity.prototype.confirmType = function(tname)
{
if (this.tname === tname) return true;
else return false;
}
Entity.prototype.constructor = Entity;
function Sprite(tname, x, y, src)
{
this.parent.constructor.call(this, tname);
this.x = x;
this.y = y;
this.img = new Image();
this.img.src = src;
this.render = function()
{
ctx.drawImage(this.img, this.x, this.y);
}
}
Sprite.prototype = Object.create(Entity.prototype);
Sprite.prototype.constructor = Sprite;
Sprite.prototype.parent = Entity.prototype;
var sprite = new Sprite("Lucario", 400, 400, "img/slot.png");
var update = function()
{
sprite.render();
}
But what I want to do is make Sprite's render function just like Entity's confirmType function, outside the constructor.
What I want to do is this:
function Sprite(tname, x, y, src)
{
...
}
Sprite.prototype.render = function()
{
ctx.drawImage(this.img, this.x, this.y);
}
Not:
function Sprite(tname, x, y, src)
{
...
this.render = function()
{
ctx.drawImage(this.img, this.x, this.y);
}
}
Basically, I want to add functions to subclasses, not just override preexisting ones. Can someone help me?
If I understand your issue, it may be purely an issue of the order of your Javascript statements. You don't show the whole sequence of code, but when you do this:
Sprite.prototype = Object.create(Entity.prototype);
That replaces the entire prototype on the Sprite object so if you had previously put any methods on the prototype, they would be wiped out by this assignment. If you then want to add more methods to the Sprite prototype, just add them after you do that (not before):
Sprite.prototype = Object.create(Entity.prototype);
Sprite.prototype.render = function() {
ctx.drawImage(this.img, this.x, this.y);
}
If you did them in the other order, it would not work:
Sprite.prototype.render = function() {
ctx.drawImage(this.img, this.x, this.y);
}
// replaces the entire prototype object, wiping out any methods that were on it
Sprite.prototype = Object.create(Entity.prototype);
I want to create a few instance of this class
var fruit = {
texture: new Image(),
speed: 5,
x: 0,
y: 0,
};
function fruits(speed, x, y)
{
fruit.speed = speed;
fruit.x = x;
fruit.y = y;
return fruit;
};
but when i create new object the all value was overridet by last created object. How can i repair this?
My loop:
var apples = [];
for(var i = 0; i < 10; i++)
{
apples[i] = new fruits(5, Math.floor((Math.random()*775)+1), 0);
apples[i].texture.src = "_img/apple.png";
}
The other answers which are appearing here are just bizarre. Here's the solution:
function fruits(speed, x, y)
{
this.texture = new Image( );
this.speed = speed;
this.x = x;
this.y = y;
};
Notice that the keyword this is used to set attributes. That means that when you call
var apple = new fruits( blah blah );
then apple will be set to a new object which has texture, speed, x and y attributes. There is no need to reference some global object to store these; they are stored in the newly created object itself.
Also I would rename it; the convention is to use singular names and a capital first letter for objects, so Fruit would make more sense (allowing new Fruit(...))
function Fruit( speed, x, y ){
var fruit = {}; // or use some base object instead of {}
fruit.texture = new Image();
fruit.speed = speed || 5;
fruit.x = x || 0;
fruit.y = y || 0;
return fruit;
};
var apples = [];
for( var i=0; i<10; i++ ){
apples[i] = Fruit( 5, Math.floor((Math.random()*775)+1), 0 );
apples[i].texture.src = "_img/apple.png";
}
Douglas Crockford - Power Constructor, 'new', 'this' and more
You got an object here:
var fruit = {
texture: new Image(),
speed: 5,
x: 0,
y: 0, // Note the superflous comma, which might break the code in some IE versions
};
And a function here:
function fruits(speed, x, y) {
fruit.speed = speed;
fruit.x = x;
fruit.y = y;
return fruit;
};
The function modifies above object whenever it is called and returns it.
Now, what you want is a constructor, but you don't have one here.
This, would be a constructor for a new Fruit:
function Fruit(speed, x, y) {
this.texture = new Image();
this.speed = speed || 5; // Note: Using logical OR to emulate default values for the argument
this.x = x || 0;
this.y = y || 0;
// Note: There is no return here!
}
var a = new Fruit(2, 1, 10);
var b = new Fruit(4, 10, 20);
a === b; // Returns false, you got two instances :)
new may have the functionality of being able to create instances of a Function, but you can still override this behavior by returning manually from within the constructor Function.
Also, even if you left out the return fruit in your original code, you would get back an empty instance of fruits since you don't assign any properties to the newly created instance.
In my Fruit example I reference the instance object via the this keyword, so I can assign speed, image, x and y to each instance created.
You might also want to read:
http://bonsaiden.github.io/JavaScript-Garden/#function.constructors
http://bonsaiden.github.io/JavaScript-Garden/#function.this
function fruits(speed, x, y) {
return {
texture: new Image(),
speed: speed,
x: x,
y: x,
}
};
Try such constructor:
function Fruit(speed, x, y) {
return {
speed: speed,
x: x,
y: y
}
}
alert(new Fruit("mySpeed", 1, 2).speed);
I want to create an instance of a Point with and without the new operator like:
Point(5, 10); // returns { x: 5, y: 10 }
// or
new Point(5, 10); // also returns { x: 5, y: 10 }
I got it working so far with the help of StackOverflow.
function Point() {
if (!(this instanceof Point)) {
var args = Array.prototype.slice.call(arguments);
// bring in the context, needed for apply
args.unshift(null);
return new (Point.bind.apply(Point, args));
}
// determine X and Y values
var pos = XY(Array.prototype.slice.call(arguments));
this.x = pos.x;
this.y = pos.y;
}
But that looks horrible, I am even unshifting null into the array so I can use apply. That just doesn't feel right.
I found a lot of solutions how to achieve it with new constructors and constructor wrappers but I want to keep it as simple as possible (it's just a plain, simple Point).
Is there an easier way to achieve this behaviour?
If you don't mind using ECMAScript 5 functions, Object.create() could help:
function Point()
{ var args = Array.prototype.slice.call(arguments);
if (this instanceof Point) return Point.apply(null, args);
var pos = XY(args);
var result = Object.create(Point.prototype);
result.x = pos.x;
result.y = pos.y;
return result;
}
If you need ECMAScript 3 compatibility, this crazy, convoluted solution is yet another one (note that it's just a wrapper for an internal equivalent of new Point):
function Point()
{ var pos = XY(Array.prototype.slice.call(arguments));
function internalPoint()
{ this.x = pos.x;
this.y = pos.y;
}
internalPoint.prototype = Point.prototype;
return new internalPoint;
}