Dynamic Class Properties in JavaScript - javascript

How do you make a class property that recalculates each time you use it?
class myClass {
constructor(x, y) {
this.x = x
this.y = y
this.percent = x/y * 100
}
}
var test = new myClass(5, 10)
test.percent
//50
test.x = 10
test.percent
//still 50
I want test.percent to change 100 and adapt to other changes.
Can I do this without turning the variable into a function?

What you are looking for is called a getter. A getter is recomputed everytime its property is accessed:
class myClass {
constructor(x, y) {
this.x = x
this.y = y
}
get percent(){
return this.x / this.y * 100
}
}
var test = new myClass(5, 10)
console.log(test.percent) //50
test.x = 10
console.log(test.percent) //100

There are two ways you can do this
You can have this.percent as a function
class myClass {
constructor(x, y) {
this.x = x;
this.y = y
this.percent = function() {
return this.x / this.y * 100
}
}
}
var test = new myClass(5, 10)
console.log(test.percent())
test.x = 10
console.log(test.percent())
You can also use getter
class myClass {
constructor(x, y) {
this.x = x;
this.y = y;
}
get percent() {
return this.x / this.y * 100
}
}
var test = new myClass(5, 10)
console.log(test.percent)
test.x = 10
console.log(test.percent)

You can use an accessor ( getter ) to alter the data each time you are accessing it.
In your case, you can replace the percent property by a getter.
class myClass {
constructor(x, y) {
this.x = x
this.y = y
}
get percent() {
return this.x / this.y * 100;
}
}
var test = new myClass(5, 10)
console.log(test.percent);
//50
test.x = 10
console.log(test.percent);
//now 100
I have also edited your code to use this to access x and y

Related

p5.js repetition of the same function

I am learning p5.js and I don't quite understand how to repeat my function on the y-axis so that the lines appeared on top of the other. I understand that I would need to make a class object but all that I succeeded to do was to freeze the editor XD. Could you help me figure out how to make my function repeat itself with different Y starting point?
let walkers = []; // creation of an array
this.xoff = 0; //changed to go outside of the walker class
this.yoff = 0; //changed to go outside of the walker class
this.x = 0;
y = 200;
function setup() {
createCanvas(600, 600);
background(250);
for (let i = 0; i < 10; i++) { //mix array and class
walkers[i] = new walker(y);
}
}
function draw() {
for (i = 0; i < walkers.length; i++) { // consider the array lenght
walker[i].acceleration(); // call the class and it's function
walker[i].velocity();
walker[i].update();
walker[i].display();
}
}
class walker {
constructor(y) { //divide the class in multiple function
this.y = y
}
acceleration() {
this.accX = 0.1;
this.accY = 0.1;
this.px = this.x;
this.py = this.y;
}
velocity() {
this.velocityY = random(-20, 20);
this.velocityX = 5;
}
update() {
this.x = this.x + this.accX + this.velocityX * noise(this.xoff);
this.y = this.y + this.accY + this.velocityY * noise(this.yoff);
}
display() {
for (this.y < 200; this.y > 400; this.y + 20) {
line(this.x, this.y, this.px, this.py);
}
this.xoff = this.xoff + 1;
this.yoff = this.yoff + 100;
this.px = this.x;
this.py = this.y;
}
}
There are quite a few things wrong with how your code behaves.
Here are a few issues:
walkers it the array used,: e.g.walkers[i].acceleration();, not walker[i].acceleration(); (same hold true for the rest of the calls)
initialize variables if you plan to use them (otherwise using math operators update() will end up with NaN: e.g. this.x, this.xoff, this.yoff, etc.
it's unclear what motion behaviour you're after with position, velocity, acceleration, perlin noise, etc. (which btw are updated with strange increments ( this.yoff = this.yoff + 100;))
The code mostly freezes because of this:
for (this.y < 200; this.y > 400; this.y + 20)
It's unclear what you're trying to do there: this.y < 200; this.y > 400 makes me think you were going for an if condition to only draw lines between 200-400 px on Y axis, however this.y + 20 makes me think you want to increment y for some reason ?
It's also unclear why x,xoff,yoff moved out the walker class ?
There may be some understanding around classes, instances and the this keyword.
As per JS naming convention, class names should be title case.
I can tell you've put a bit of effort trying to build a more complex sketch, however it will won't be helpful for you to add complexity unless you understand all the parts of your code. This is where mastering the fundamentals pays off.
I recommend:
going back to pen/paper sketching to work out your idea until it's clearer
thinking of how you will achieve that by breaking complex tasks into smaller steps
writing basic test sketches for each step/component
Finally, bring one component at a time into a main program, testing again as components interact with another. Be sure to check out Kevin Workman's How to Program guide.
FWIW, here's a modified version of your code, with a tweak to set the initial y position of each Walker on top of each other:
let walkers = []; // creation of an array
y = 200;
function setup() {
createCanvas(600, 600);
background(250);
for (let i = 0; i < 10; i++) { //mix array and class
// incrementally add 10 pixels to y so the initially lines start on top of each other
walkers[i] = new Walker(y + (i * 10));
}
}
function draw() {
for (let i = 0; i < walkers.length; i++) { // consider the array length
// walkers, not walker
walkers[i].acceleration(); // call the class and it's function
walkers[i].velocity();
walkers[i].update();
walkers[i].display();
}
}
class Walker {
constructor(y) { //divide the class in multiple function
this.y = y;
// remember to init all variables you plan to use
this.xoff = 0; //changed to go back inside of the walker class
this.yoff = 0; //changed to go back inside of the walker class
this.x = 0;
}
acceleration() {
this.accX = 0.1;
this.accY = 0.1;
this.px = this.x;
this.py = this.y;
}
velocity() {
this.velocityY = random(-20, 20);
this.velocityX = 5;
}
update() {
this.x = this.x + this.accX + this.velocityX * noise(this.xoff);
this.y = this.y + this.accY + this.velocityY * noise(this.yoff);
}
display() {
// what were you trying ?
//for (this.y < 200; this.y > 400; this.y + 20) {
line(this.x, this.y, this.px, this.py);
//}
this.xoff = this.xoff + 1;
this.yoff = this.yoff + 1;
this.px = this.x;
this.py = this.y;
// reset x, y to 0
if(this.x > width){
this.x = 0;
}
if(this.y > height){
this.y = 0;
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.min.js"></script>
It will definitely be easier to do it using a class. Make different functions inside the class which are responsible for update, movement, etc. You can make a display function too in which you can set the y co-ordinate using a For loop. In that way, it will become very easy to keep changing the y co-ordinate.
If you want to display multiple lines at once, do all of the above and also use an array to store the y co-ordinates and then display them in the For loop.
Do let me know if you need help with the actual code.

How does Prototypical Inheritance works in JS

I am trying prototypical inheritance in order to calculate distance(). But I am getting an error which is TypeError: Cannot read property 'x' of undefined. The error line is marked at the code. I know that the way I am calculating distance() is wrong. But someone can guide me what is the correct way?
'use strict';
function Shape(x,y){
this.x = x;
this.y = y;
};
Shape.prototype.distance = function(s1,s2){
// this.distance = function(s1,s2){
const xDiff = this.s1.x - this.s2.x; //error
const yDiff = this.s1.y - this.s2.y;
return Math.sqrt(xDiff*xDiff + yDiff*yDiff);
// }
};
function Circle(x,y,radius){
Shape.call(this,x,y);
this.radius = radius;
this.area = Math.PI*this.radius*this.radius;
};
function Rectangle(x,y,w,h){
Shape.call(this,x,y);
this.width =w;
this.height =h;
this.area = this.width * this.height;
};
Circle.prototype = Object.create(Shape.prototype);
Circle.prototype.constructor = Circle;
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;
const shapes = [
new Rectangle(3,4,5,6),
new Circle(0,0,1),
];
shapes.forEach((s) => console.log(s.x, s.y, s.area));
console.log(Shape.prototype.distance(shapes[0],shapes[1]));
Typically when you put a method on the prototype you anticipate it being called on an instance of that class. So, in your example you would do something like:
someShape.distance(someOtherShape)
When you do that the this in the function refers to the shape on which you called the function. So your distance function would look like this:
Shape.prototype.distance = function(otherShape){
const xDiff = this.x - otherShape.x;
const yDiff = this.y - otherShape.y;
return Math.sqrt(xDiff*xDiff + yDiff*yDiff);
}
Then you can call it as a method on one of the objects:
function Shape(x, y) {
this.x = x;
this.y = y;
}
Shape.prototype.distance = function(otherShape) {
const xDiff = this.x - otherShape.x; //error
const yDiff = this.y - otherShape.y;
return Math.sqrt(xDiff * xDiff + yDiff * yDiff);
}
let sh1 = new Shape(0, 0)
let sh2 = new Shape(3, 4)
console.log(sh1.distance(sh2))
// or the opposite
console.log(sh2.distance(sh1))
If instead you want distance to be a class method and you want to pass in two instances, don't put the function on the prototype, add it as a property of the function itself:
function Shape(x,y){
this.x = x;
this.y = y;
}
// not on the prototype
Shape.distance = function(s1, s2){
const xDiff = s1.x - s2.x;
const yDiff = s1.y - s2.y;
return Math.sqrt(xDiff*xDiff + yDiff*yDiff);
}
let sh1 = new Shape(0, 0)
let sh2 = new Shape(3, 4)
console.log(Shape.distance(sh1, sh2))

How do i call a function on another function

I am trying to call a function does multiplication On another function that accepts two numbers as parameters. i Have tried using constructor and nested function, but to no avail. i have tried the followings:
function Coordinate(a, b) {
var x, y;
return {x: a, y: b};
function multiply(n) {
x * n;
y * n;
}
}
var makeCoordinate = new Coordinate(2,3);
console.log(makeCoordinate.multiple(2));
// expected output: 4 6;
You should set multiply to be on the Coordinate's prototype so that when you call new Coordinate, the instantiated object will have multiply as a method. For it to work, you should also set this.x and this.y instead of returning an object directly:
function Coordinate(a, b) {
this.x = a;
this.y = b;
}
Coordinate.prototype.multiply = function(n) {
this.x *= n;
this.y *= n;
return this;
}
var makeCoordinate = new Coordinate(2,3);
console.log(makeCoordinate.multiply(2));
Or, if you wanted multiply to return just the multiplied coordinates without changing the original object, then return the coordinates alone:
function Coordinate(a, b) {
this.x = a;
this.y = b;
}
Coordinate.prototype.multiply = function(n) {
return [this.x * n, this.y * n];
}
var makeCoordinate = new Coordinate(2,3);
console.log(makeCoordinate.multiply(2));
The answer modified two parts:
the creation of Coordinate members
multiple -> multiply
Hope this help :)
function Coordinate(a, b) {
this.x = a;
this.y = b;
this.multiply = function(n) {
return this.x * n + " " + this.y * n;
}
}
var makeCoordinate = new Coordinate(2,3);
console.log(makeCoordinate.multiply(2));
Well, firstly your console.log is calling multiple, not multiply.
Second, try an approach like this:
function Coordinate( a, b )
{
function multiply( n )
{
this.x = this.x * n;
this.y = this.y * n;
return this;
}
var co = { x: a, y : b };
co.multiply = multiply.bind( co );
return co;
}
Having clarified in the comments, the (simplest)solution is:
// important code
function Coordinate(a,b) {
this.x = a;
this.y = b;
this.multiply = function (n) {
this.x = this.x*n;
this.y = this.y*n;
}
}
// test code
let coord = new Coordinate(2,3);
console.log("Original Coordinate:");
console.log(coord);
coord.multiply(2);
console.log("Changed Coordinate: ");
console.log(coord);
You could also put the function in the prototype if you don't want a copy of it in every Coordinate object.

best design practice to separate common code?

I have a card class:
function Card() {
this.image = new Image();
this.x = 0;
this.y = 400;
this.initialX = 0;
this.initialY = 0;
this.scaleFactor = 4;
this.setImage = function(ii){
this.image.src = ii;
};
this.getWidth = function(){
if (this.image == null){
return 0;
}
return this.image.width / this.scaleFactor;
}
this.getHeight = function(){
if (this.image == null){
return 0;
}
return this.image.height / this.scaleFactor;
}
this.pointIsInCard = function(mx, my){
if (mx >= this.x && mx <= (this.x + this.getWidth()) && my >= this.y && my <= (this.y + this.getHeight()))
{
return true;
}
else{
return false;
}
};
};
I then have a deck class:
function Deck(x, y, w, h){
this.x = x;
this.y = y;
this.width = w;
this.height = h;
this.cards = [];
}
I need to add a method in Deck class similar to pointIsInCard instead it will be called pointIsInDeck. The logic will be same i.e to check whether the passed in point falls in the boundary of the object. So seeing this duplication of code I wanted to know what is a good design practice to avoid this duplication? One option I thought of was to extract the method out and create a function for generic object with x, y, width, height but again from OOP principles I thought this method should belong to the class/object. I appreciate any help! Thanks!
A common approach for what you're doing is to attach a Rectangle or similar instance with that functionality to both of your objects, that is:
class Rectangle {
constructor(x, y, width, height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
containsPoint(x, y) {
return x >= this.x && x =< this.width &&
y >= this.y && y =< this.height;
}
}
Then just add it to Card and Deck:
function Card() {
this.rect = new Rectangle(/* Your card shape */);
// ...
}
function Deck() {
this.rect = new Rectangle(/* Your deck shape */);
// ...
}
And you can do:
card.rect.containsPoint();
deck.rect.containsPoint();
If these are classes related to drawing, they would both inherit from something like Rectangle, which they would both inherit this behaviour from.
If they are gameplay-related, I would prefer them each referencing a Rectangle (or its subclass) that they would delegate all UI-related tasks to; then reduce this to the previous paragraph's solution.
You can use Function.prototype.call() to set this at a function call
function Card() {
this.x = 1; this.y = 2;
};
function Deck() {
this.x = 10; this.y = 20;
}
function points(x, y) {
// do stuff
console.log(x, this.x, y, this.y); // `this`: `c` or `d`
}
var c = new Card();
var d = new Deck();
points.call(c, 3, 4); // `this`: `c` within `points` call
points.call(d, 100, 200); // `this`: `d` within `points` call

Constructor function that creates circle objects javascript

I want to make a constructor function that creates circle objects. I need to add two methods to it. I want first method (translate()) to takes two number parameters and adds the first one to the Circle's x-coordinate and adds the second one to the Circle's y-coordinate.
and I want the second method to take a Circle parameter and return yes if the two Circles intersect, but return false otherwise.
function Circle (xPoint, yPoint, radius) {
this.x = xPoint;
this.y = yPoint;
this.r = radius;
}
function translate(horiz, vert) {
return ((horiz + this.x) && (vert + this.y));
}
How would I implement the second, intersects(), method?
Here you go:
function Circle(x, y, r) {
this.x = x;
this.y = y;
this.r = r;
this.translate = function(h, v) {
this.x += h;
this.y += v;
};
this.intersect = function(circle) {
var centerDistance = Math.pow(this.x - circle.x, 2) + Math.pow(this.y - circle.y, 2);
return Math.pow(this.r - circle.r, 2) <= centerDistance && centerDistance <= Math.pow(this.r + cirle.r, 2);
};
}

Categories