Object-oriented Javascript - javascript

this is my first attempt at oo javascript:
function GuiObject() {
this.x = 0;
this.y = 0;
this.width = 0;
this.height = 0;
this.parent = null;
this.children = [];
this.getWidth = function()
{
return this.width;
};
this.getHeight = function()
{
return this.height;
};
this.paint = function(ctx)
{
};
};
function paintGui(ctx, root)
{
ctx.save();
ctx.translate(root.x, root.y);
root.paint(ctx);
for (int i=0; i<root.children.length; ++i)
{
paintGui(ctx, root.children[i]);
}
ctx.restore();
};
Now in the paintGUI function, the line root.children.lengths throws an error:
Uncaught SyntaxError: Unexpected identifier.
What did i do wrong?
Thanks!

It's hard to say what your actual problem is without looking at the code that actually constructs a GuiObject but for what it's worth, here's a better way to write that 'class'.
function GuiObject() {
this.x = 0;
this.y = 0;
this.width = 0;
this.height = 0;
this.parent = null;
this.children = [];
}
GuiObject.prototype.getWidth = function()
{
return this.width;
};
GuiObject.prototype.getHeight = function()
{
return this.height;
};
GuiObject.prototype.paint = function(ctx)
{
};
Doing it this way, every instance can share the same methods. The other way, you would be creating new function objects for every instance you created. The only reason to ever define the methods in the constructor instead of attaching them to the prototypes is if they need to have access to private members that don't ever get attached to this.

int i? What's that supposed to mean in Javascript then? I think you meant var i.
BTW, In common with all the other people who responded, I looked at your code and didn't spot it immediately. What I then did was copy/paste your function into the Javascript Console and gradually removed lines until it stopped complaining. It's a useful technique to try out little bits of javascript.

Related

Object in Javascript undefined when calling a method

I'm building a game similar to the Chrome dinosaur in Vanilla JS. To animate the obstacles I have created a class Obstacle, which stores their position and size, and defines a method that changes the position.
var Obstacle = function (type, w, h, sprite) {
this.h = h; // Obstacle height
this.w = w; // Obstacle width
this.x = 600; // Starting horizontal position
this.y = GROUND - this.h; // Starting vertical position
this.type = type;
this.sprite = sprite;
this.speed = -4;
this.move = function () {
this.x += this.speed;
}
}
These are stored inside an array, defined as a property of a different class:
var ObstacleBuffer = function () {
this.bufferFront = [];
this.createObstacle = function () {
this.bufferFront.push(this.createBox());
}
// Obstacle creators
this.createBox = function () {
if (Math.random() < 0.5) return new Obstacle ("box1", OBSTACLES.box1.w, OBSTACLES.box1.h, OBSTACLES.box1.sprite);
return new Obstacle ("box2", OBSTACLES.box2.w, OBSTACLES.box2.h, OBSTACLES.box2.sprite);
}
//Obstacle animation
this.animateObstacle = function () {
this.bufferFront[0].move();
}
}
When running this an error pops up:
TypeError: Cannot read property 'move' of undefined.
I have logger the content of this.bufferFront[0] and it correctly show the Obstacle stored inside it.
I have also tried assigning this.bufferFront[0] locally to a variable and then tried to call the method from there. The data stored is correct but the error pops up whenever trying to access Obstacles methods or properties.
Any ideas ? Thank you very much.
EDIT - I have reviewed the code as per your suggestions and the problem seems to be at the point where I'm calling the function. Before generating the obstacles I am preloading a series of images and only generating obstacles once these have load:
this.loadWhenReady = function () {
if (self.resources.isLoadComplete()) {
self.sound.load(self.resources.list.sfx);
drawGround();
this.obstacles.createObstacle(); // <--
self.startGame();
return;
} else {
setTimeout(self.loadWhenReady, 300);
}
}
And this is called in:
setTimeout(self.loadWhenReady, 300);
Of course const self = this has been defined before.
Everything seems to move forward when the method is called outside the SetTimeout.
Why is this happening though ? And is there a way of solving this while calling the method in there ?
SOLVED - As #Bergi and #Jaime-Blandon mention it was a context problem. Calling the method from outside the setTimeout loop or using self.obstacle.createObstacle() instead of this.obstacle.createObstacle() did the trick and solved the issue.
Try this change on the ObstacleBuffer Class:
var ObstacleBuffer = function () {
this.bufferFront = [];
this.createObstacle = function () {
//this.bufferFront.push(this.createBox());
this.createBox();
}
// Obstacle creators
this.createBox = function () {
if (Math.random() < 0.5) this.bufferFront.push(new Obstacle ("box1", OBSTACLES.box1.w, OBSTACLES.box1.h, OBSTACLES.box1.sprite));
this.bufferFront.push(new Obstacle ("box2", OBSTACLES.box2.w, OBSTACLES.box2.h, OBSTACLES.box2.sprite));
}
//Obstacle animation
this.animateObstacle = function () {
this.bufferFront[0].move();
}
}
Best Regards!

Passing Variable to Asynchronous JavaScript Function Invoked in Loop (React)

I'm writing some JavaScript code for my React project. The code loads a series of images, then updates the state with the dimensions of each image. The problem is that when I invoke the onload function, the this keyword refers to the object attached to the onload. This means I can no longer access props through this.props. Is there a way to pass the props into the function?
Here's my code:
for (var i = 0; i < a; i++) {
var path = i + ".jpg";
imgArray[i].index = i;
imgArray[i].onload = function() {
this.props.actions.updateImage(this.index, this.width, this.height);
}
imgArray[i].src = path;
}
I currently get an error, as this.props is undefined, since this refers to imgArray[i] in the function, not the global context.
A simple solution might just be to save the context or props into a variable and use them:
const { props } = this;
// ...
imgArray[i].onload = function() {
props.actions.updateImage(this.index, this.width, this.height);
}
You can also save the other context if you find that more readable:
const ctx = this;
// ...
imgArray[i].onload = function() {
ctx.props.actions.updateImage(this.index, this.width, this.height);
}
Your best bet is to capture the props by using a variable that holds a reference to the outer 'this' that you access via the closure:
// This line here, now inside the function, use 'self' to refer to outer context
let self = this;
for (var i = 0; i < a; i++) {
var path = i + ".jpg";
imgArray[i].index = i;
imgArray[i].onload = function() {
// note call to self.props instead of this.props:
self.props.actions.updateImage(this.index, this.width, this.height);
}
imgArray[i].src = path;
}

How to implement multiple new functions in Javascript object?

I am doing some reading about class creation in Javascript. I know the concept does not exist in Javascript and that one can work with prototype.
I am trying to translate the following piece of code from Java to Javascript. Specifically, I want to have two constructors, one parameterless and one with two parameters:
public class MyClass {
int width = 10;
int height = 20;
public MyClass() { };
public MyClass(int w, int h) {
this.width = w;
this.height = h;
};
...
}
As far as I understand, I need to define my 'class' as following in Javascript:
function MyClass() {
this.width = 10;
this.height = 20;
};
But, how do I define my second constructor? I want to be able to create instances of my class two ways:
var Instance1 = new MyClass();
var Instance2 = new MyClass(33,45);
Update:
Ok, I understand my constructors cannot have the same name, because Javascript cannot recognize the different parameter types. So, if I use different names for my constructors, how am I supposed to declare them? Is the following correct?
function MyClass() {
this.width = 10;
this.height = 20;
};
MyClass.prototype.New2 = function(w,h) {
var result = new MyClass();
result.width = w,
result.height = h,
return result;
};
Javascript has no multimethods, therefore your only option is to parse arguments and act accordingly. A common idiom is to use || to check if an argument is "empty" (undefined or 0):
function MyClass(w, h) {
this.width = w || 10;
this.height = h || 20;
};
If 0 is a valid value in your context, check for undefined explicitly:
function MyClass(w, h) {
this.width = typeof w != 'undefined' ? w : 10;
this.height = typeof h != 'undefined' ? h : 20;
};
Another option is to provide arguments as an object and merge it with the "defaults" object. This is a common pattern in jquery:
function MyClass(options) {
// set up default options
var defaults = {
width: 10,
height: 20
};
var options = $.extend({}, defaults, options);

Javascript constructor not working

I posted this on gamedev.stackexchange but was referred here so I'll try. I've got this simple menu that is a function, with a mainmenu.prototype.Render to draw it to the screen. Inside the mainmenu function i would like to make an array of objects containing the buttons x, y positions and the .src.
This is my current code that works, so no problem with the function itself:
this.Mainmenu = function() {
}
this.Mainmenu.prototype.Render = function() {
imgPause = new Image();
imgPause.src = 'img/pause.png';
c.drawImage(imgPause, canvas.width - 42, 10);
}
var mainmenu = new self.Mainmenu();
What I would like the final result to look like, but can't get to work (I've included the error in a comment):
this.Mainmenu = function() {
this.button = function(src, X, Y) {
this = new Image(); // Gives error "Invalid left-hand side in assignement"
this.src = src;
this.X = X;
this.Y = Y;
}
this.buttons = [pause = new this.button(src, X, Y)];
}
this.Mainmenu.prototype.Render = function() {
for (i = 0; i < this.buttons.length; i++) {
c.drawImage(this.src, this.X, this.Y);
}
}
var mainmenu = new self.Mainmenu();
But it doesn't work, if anyone can identify where my mistake is it would be appreciated, my patience is about to run out.
Well, your mistake is exactly what your js interpreter says it is - the left side of your assignment is invalid. Namely, you cannot assign this to anything, that's a rule of thumb in all languages that have the this word. The reasoning behind that is obvious - this denotes the current context of the function, the hidden argument of its. If you could overwrite it dynamically, you could alter the behaviour of every single function that is using yours thus the whole program.
How not to use this in this broken way:
this.MainMenu = function() {
this.Button = function(src, X, Y) {
var image = new Image();
image.src = src;
image.X = X;
image.Y = Y;
return image;
}
this.buttons = [pause = new this.Button(src, X, Y)];
}
Also, name your classes with PascalCase (Button, not button) and your variables with camelCase EVERYWHERE (x, not X).
You cannot do this
this.button = function(src, X, Y) {
this = new Image(); // Gives error "Invalid left-hand side in assignement"
}
this represents the current instance of Mainmenu. You cannot override an instance by another
instance.
No sense.

Updating an object's definition in javascript

I'm fairly new to object orientated stuff so this may very well be the wrong way to be going about getting this done.
This is a very slimmed down version of what I currently have, but the concept is essentially the same. When the user clicks on a canvas element on my page, I create 20 instances of the particle object below, append them to an array whilst at the same time updating the canvas at 30FPS and drawing circles based on the x property of the instances of each object. Once a particle is off the screen, it's removed from the array.
var particle = function()
{
var _this = this;
this.velocity = 1;
this.x = 0;
this.updateVelocity = function(newVelocity)
{
_this.multiplier = newVelocity;
}
var updateObject = function()
{
_this.x += velocity;
}
}
I would like the user to be able to control the velocity of new particles that are created using an input element on the page. When this is updated I have an event listener call
particle.updateVelocity(whateverTheUserEntered);
However I get the error "particle has no method updateVelocity". After a bit of reading up on the subject I understand that to call that function I need to create an instance of the object, but this will only update the velocity value of that instance which isn't going to work for me.
My question is, is there a way to achieve what I'm doing or have I approached this in completely the wrong way? As I said, I'm still getting to grips with OOP principles so I may have just answered my own question...
Try this:
var particle = new (function()
{
var _this = this;
this.velocity = 1;
this.x = 0;
this.updateVelocity = function(newVelocity)
{
_this.multiplier = newVelocity;
}
var updateObject = function()
{
_this.x += velocity;
}
})();
Your's is creating a function and then setting the variable particle to that value. particle will not have any special properties because of this. My example above, however, by using new and the function as a constructor assigns particle an instance of a (now anonymous) class.
I think what you want is:
// define a particle "class"
function Particle() {
var _this = {};
_this.velocity = 1;
_this.x = 0;
_this.multiplier = 1;
_this.updateVelocity = function(newVelocity)
{
_this.multiplier = newVelocity;
}
_this.updateObject = function()
{
_this.x += velocity;
}
return _this;
}
// make 1 particle
var myParticle = new Particle();
myParticle.updateVelocity(100);
// make a bunch of particles
var myParticles = [];
for (var i=0; i < 100; i++) {
var p = new Particle();
p.updateVelocity(Math.random * 100);
myParticles.push(p);
}
If you change it to
var particle = new function () {
}
The 'new' will cause creation of an instance.
So create a function that builds new particle instances for you.
Make velocity static and have a static method to update it. This way, you can still make instances of particle and update the velocity for all of them.
var particle = function() {
// particle stuff
}
particle.velocity = 1;
particle.updateVelocity = function(newVelocity) {
this.velocity = newVelocity
}

Categories