Class method is not a function? - javascript

I'm getting "Uncaught TypeError: this.time_to_x is not a function" when incorporating some open source ES5 code into my ES6 Class. Here is the class (I've removed some of the bulk, but most of the essential stuff is there). Assume Diamond() is called. It's this line that gets the error: x = this.time_to_x(frame.time);
Why is time_to_x() not being considered a function?
export default class TimelinePanel {
constructor(ctx) {
this.ctx = ctx;
this.ctx_wrap = ctx;
}
create (ctx) {
this.rect({ctx, x: 20, y: 15, width: 130, height: 10}); // ***
this.drawLayerContents();
}
Diamond(frame, y) {
var x, y2;
x = this.time_to_x(frame.time);
y2 = y + LINE_HEIGHT * 0.5 - DIAMOND_SIZE / 2;
var self = this;
var isOver = false;
this.path = function() {
this.ctx_wrap
.beginPath()
.moveTo(x, y2)
.lineTo(x + DIAMOND_SIZE / 2, y2 + DIAMOND_SIZE / 2)
.lineTo(x, y2 + DIAMOND_SIZE)
.lineTo(x - DIAMOND_SIZE / 2, y2 + DIAMOND_SIZE / 2)
.closePath();
};
}
drawLayerContents() {
// ...
for (i = 0; i < il; i++) {
// ...
for (j = 0; j < values.length; j++) {
// Dimonds
frame = values[j];
renderItems.push(new this.Diamond(frame, y));
}
}
}
y_to_track(y) {
if (y - MARKER_TRACK_HEIGHT < 0) return -1;
return (y - MARKER_TRACK_HEIGHT + scrollTop) / LINE_HEIGHT | 0;
}
x_to_time(x) {
var units = time_scale / tickMark3;
return frame_start + ((x - LEFT_GUTTER) / units | 0) / tickMark3;
}
time_to_x(s) {
var ds = s - frame_start;
ds *= time_scale;
ds += LEFT_GUTTER;
return ds;
}
}

You are creating an instance of this.Diamond class when you do new this.Diamond(frame, y). As a result, inside the function, this is this new instance, not the instance of TimelinePanel where it has originally been created from. Hence, this does not have the members of TimelinePanel.
Because it seems y_to_track and x_to_time does not make use of this, you could make them static (add the keyword static before them) and call them as follow: TimelinePanel.y_to_track.
If you need to access methods bound to a particular instance of TimelinePanel, then I don't see any other solution than passing this instance to the Diamond constructor or refactoring TimelinePanel and use closure around the Diamond constructor.
In any case it seems you are trying to replicate the behavior of Java-like internal classes (e.g. where you can access the container class instance with ClassName.this or just access the container class members), there is no such things in JS (at least with class).
EDIT: I just noticed that you are accessing TimelinePanel's ctx_wrap member that you will not be able to put as class member. The easiest seem to pass the TimelinePanel to the Diamond constructor: Diamond(frame, y, panel) and new this.Diamond(frame, y, this). It puts into question the usefulness of adding Diamond as a member of TimelinePanel.

Because the way you have it it's supposed time_to_x from closure, not from this. In this there is no such function, so this.time_to_x name returns undefined which is not a function indeed.
I suggest smth like this:
put var self = this; inside the class but outside of the Diamond method.
Then call self.time_to_x() inside Diamond.

Related

Is it a good practice in Javascript to have constructor function having the same name as the object it creates?

Let's say we have a code
function Vector ( x, y )
{
this.x = x
this.y = y
}
var Vector = new Vector()
Is it okay in general to have object Vector having the same name as it's constructor function?
It is not a good practice to use the same name as the instanciable function, because
it is confusing, because you change the type of the variable from instanciable to instance,
it violates the good practice to name instances with starting small letters,
it make the instanciable function inaccessable.
To prevent confusion, you could take an IIFE as constructor.
var vector = new function (x, y) {
this.x = x
this.y = y
};
console.log(vector);
Your instance shadows the constructor function. In other words, you can no longer access the constructor function after creating the instance unless you try to do it via the constructor of your Vector instance.
function Vector ( x, y )
{
this.x = x
this.y = y
}
var Vector = new Vector()
var AnotherVector = new Vector(); // <-Error here
All above leads to confusion and lack of standard JS practice.
No - don't do it.
Defining a class for a single instance sounds useless. A class is supposed to act as a template to create multiple instances of the same type. What would you do if you want a second vector?
Vector = function (x, y) {
this.x = x;
this.y = y;
};
Vector = new Vector(1, 2); // ok
Vector = new Vector(4, 3); // error
Moreover, a class is usually the place where you define a common API (a common set of methods) for all the instances.
Vector = function (x, y) {
this.x = x;
this.y = y;
};
// Note that in old fashioned JavaScript
// you have to define inherited methods
// in a special object called `prototype`.
Vector.prototype.add = function (vector) {
this.x += vector.x;
this.y += vector.y;
};
Vector = new Vector(1, 1);
You don't really need this feature for a single instance. Using a class here is overkill, you could simply write the following code instead:
Vector = {
x: 1,
y: 1,
add: function (vector) {
this.x += vector.x;
this.y += vector.y;
}
};
Therefore, I would say that overwriting a class with an instance is not a good practice, unless this pattern has some useful applications that I have never heard of :-)
Anyway, here is the recommended (old fashioned) way of using classes in JavaScript. As you can see, the add method is defined once in the prototype of the Vector class, but we can call it from both vectors a and b.
Vector = function (x, y) {
this.x = x;
this.y = y;
};
Vector.prototype.add = function (vector) {
this.x += vector.x;
this.y += vector.y;
};
Vector.prototype.toString = function () {
return "(" + this.x + ", " + this.y + ")";
};
a = new Vector(1, 2);
b = new Vector(4, 3);
console.log("a = " + a + " b = " + b);
a.add(b);
console.log("a = " + a + " b = " + b);
b.add(a);
console.log("a = " + a + " b = " + b);
No, It's not a good practice.
Because JavaScript is case sensitive, consider using all lowercase letters in your variable names. This ensures that you never run into errors because you misused uppercase and lowercase letters, plus it's easier on the typing fingers.
The two standard conventions for overcoming this are to capitalize each word and cram them together (for example, LastName) or to separate each word with an underscore (for example, last_name).
Good practice:
function Vector ( x, y )
{
this.x = x ;
this.y = y;
}
var vector = new Vector(1, 2);
console.log(vector);
Vector will no more be a function, so NO. you surely don't want to do that.
Check this
function Vector ( x, y )
{
this.x = x
this.y = y
}
var Vector = new Vector()
var Vector2 = new Vector()
Better if call your object same name but starting from lowercase letter
Since objects are instances I would call them different. It's up to your usecase, so if know that you will have just one instance then you can do it.
But imagine there are multiple instances, then it would make sence to call them different.
So for example you have one object with high values of x and y:
var highVector = new Vector(1000, 1000)
You are still using the word Vector but now you know what kind of Vector this one is.
And the object with low values can be called lowVector and so on.

Using 'This' within an IIFE constructor

I'm working on a small retro-style side-scrolling space shooter game (or, that's the theory anyway) and I've recently moved over to using IIFEs for managing my separate 'classes'.
However, most of the examples I've seen tend to use var when declaring variables, E.g, var x = 0. I'm wondering though, is it possible to use this.x = 0 and if so, are there any benefits or drawbacks?
I've tried googling it, and can't find much on the subject, which leads me to think it's a non-issue.
My classes are as follows;
var Player = function () {
// ------------------------------------------------------------------------------------------------
// PLAYER VARIABLES
// ------------------------------------------------------------------------------------------------
var w = 50;
var h = 50;
var x = 0;
var y = 0;
var color = 'white';
var projectiles = [];
// ------------------------------------------------------------------------------------------------
// BIND EVENTS TO THE GLOBAL CANVAS
// ------------------------------------------------------------------------------------------------
Canvas.bindEvent('mousemove', function(e){
y = (e.pageY - Canvas.element.getBoundingClientRect().top) - (h / 2);
});
Canvas.bindEvent('click', function(e){
createProjectile(50, (y + (h / 2)) - 10);
});
// ------------------------------------------------------------------------------------------------
// FUNCTIONS
// ------------------------------------------------------------------------------------------------
var createProjectile = function(x, y){
projectiles.push({
x: x,
y: y
})
};
var update = function(){
for(var p = projectiles.length - 1; p >= 0; p--){
projectiles[p].x += 10;
if(projectiles[p].x > Canvas.element.width)projectiles.splice(p, 1);
}
};
var render = function () {
Canvas.context.fillStyle = color;
Canvas.context.fillRect(x, y, w, h);
console.log(projectiles.length);
for(var p = 0; p < projectiles.length; p++){
Canvas.context.fillStyle = 'red';
Canvas.context.fillRect(projectiles[p].x, projectiles[p].y, 20, 20);
}
};
// ------------------------------------------------------------------------------------------------
// Exposed Variables and Functions
// ------------------------------------------------------------------------------------------------
return{
update: update,
render: render
}
}();
are there any benefits or drawbacks?
The drawbacks are that in strict mode, you will get a runtime error (because this is undefined).
In non-strict mode, this will refer to window, so this.x = ... creates a global variable (which is what you want to avoid with the IIFE in the first place I guess).
There are no benefits.

Define a unique method to an instance via prototype

I have an Object which called Rectangle :
function Rectangle(x, y) {
this.x = x;
this.y = y ;
this.surface = function(x, y) {
return x*y;
}
}
Rectangle.prototype.couleur = "Rouge";
and I have two instances of this Object :
r1 = new Rectangle(3, 5);
r2 = new Rectangle(4, 7);
Then I declared a third instance :
r3 = new Rectangle(6, 7);
and I want this instance to have a unique method :
afficheCouleur = function() {
return this.couleur;
}
I tried as this :
r3.prototype.afficheCouleur = function() {
return this.couleur;
}
But I got this error :
[11:32:40.848] TypeError: r3.prototype is undefined # file:///media/tpw/760F-F396/vv:24
Just declare the method directly:
r3.afficheCouleur = function() {
return this.couleur;
}
This happens because prototype is a property of constructors (functions) and r3 is an object.
If you do that you will end up with 2 Rectangles with a different implementation.
Create a second class that inherits from the first:
function Rectangle(x, y) {
this.x = x;
this.y = y ;
this.surface = function(x, y) {
return x*y;
}
}
Rectangle.prototype.couleur = "Rouge";
function ColourRectangle(x, y) {
Rectangle.apply(this, arguments);
this.afficheCouleur = function() {
return this.couleur;
}
}
ColourRectangle.prototype = new Rectangle();
ColourRectangle.prototype.constructor = Rectangle;
var a = new Rectangle(1, 2);
console.log(a.y); //2
console.log(a.afficheCouleur); //undef
var b = new ColourRectangle(3, 4);
console.log(b.y); //2
console.log(b.afficheCouleur()); // Rouge
Assuming that you might re factor your service function to use the Rectangle's x and y instead of passing them (why would you pass them if you want to know the Rectangle's surface?).
One problem you may have declaring service in the body like this is that it doesn't update x and y when you change x and y of your Rectangle instance. This because x and y are remembered in the closure scope.
You may end up with code like this:
function Rectangle(x, y) {
this.x = x;
this.y = y ;
this.surface = function() {
return x*y;
}
}
var r = new Rectangle(5,5);
console.log(r.surface());//=25
r.y =500;
console.log(r.surface());//=25/
console.log(r.x*r.y);//=2500
you should return this.x * this.y in the surface method but since you are not using closures to simulate private members you may as well put the function on the prototype.
function Rectangle(x, y) {
this.x = x;
this.y = y ;
}
Rectangle.prototype.surface = function() {
return this.x*this.y;
}
var r = new Rectangle(5,5);
console.log(r.surface());//=25
r.y =500;
console.log(r.surface());//=2500
console.log(r.x*r.y);//=2500
I know this doesn't answer your question but you are asking to add a method that will be shared among instances (prototype) uniquely on an instance. This can't be done as Danilo and Basha pointed out already.
The link posted in my comment to your question may help you understand better what prototype is, how the members there are used and what instance specific members are.

Why's this object not working?

box = new Object();
box.height = 30;
box.length = 20;
box.both = function(box.height, box.length) {
return box.height * box.length;
}
document.write(box.both(10, 20));
Well as the title says.
First off I'd created an object.
Made to properties, height and length.
Assigned a value to each.
Made a method BOTH
In function I'd put 2 arguments which are object properties.
Returned their product.
And finally called the function giving it numerical value..
Why's this not working :(
The problem is:
box.both=function(box.height,box.length){
box.height and box.length are not valid names for function parameters. This should be:
box.both=function(h, l) {
return h * l;
}
However, it seems you might be looking to get the area of the current box instance. In that case, you don't need any parameters:
box.both=function() {
return this.height * this.length;
}
document.write(box.both());
I think you probably want it this way:
box = new Object();
box.height = 30;
box.length = 20;
box.both = function(height,length){
this.height = height;
this.length = length;
return height*length;
}
document.write(box.both(10,20));
box = new Object();
box.height = 30;
box.length = 20;
box.both = function() {
return box.height * box.length;
}

When two objects are dependent on each other? How to de-couple them?

Bit of a generic question but non the less I am in a situation where I do not know what to do and google has failed me!
I am trying to re-write a grid array collision with canvas that I built.
Now there is a grid object and a block object. The grid cellSize is dependent on being the same size of the block size and vice versa. The reason being is that to work out the grid array to store the blocks into I must first work out how to build it and that is dependent on the size of the block. Example,
var grid = new grid();
function grid() {
this.cellSize = 50;
this.cellsX = canvas.width / this.cellSize;
this.cellsY = canvas.height / this.cellSize;
this.buildGrid = function() {
var arr = new Array(this.cellsX);
for(var i = 0; i < arr.length; ++i) {
arr[i] = new Array(this.cellsY);
}
return arr;
};
this.arr = this.buildGrid();
this.getGridCoords = function(i) {
return Math.floor(i / this.cellSize);
};
}
function block() {
this.size = grid.cellSize; // size of the block is same as cellSize
this.x = 0;
this.y = 0 - (this.size * 1.5);
this.baseVelocity = 1;
this.velocity = this.baseVelocity;
this.update = function() {
this.y += this.velocity;
};
}
Doing it the way I have done it couples the two objects together and from what I have percieved that is a bad thing. How can I make sure that the two variables are the same size without coupling the objects if that makes sense?
The real issue is that your block() function is taking a value directly from an instance of grid().
If you want your block() function to be reusable and decoupled, its as easy as changing block() to take the size during construction.
arr[i] = new block({size: this.cellSize});

Categories