Im trying to invoke method inside constructor, is it not possible or have i missed something?
function Rectangle(height, width) {
this.height = height;
this.width = width;
this.calcArea = function() {
console.log(this.height);
return this.height * this.width;
};
this.calcArea(); // trying to do it here, its invoked but no result
}
var newone = new Rectangle(12,24);
You can try something like this:
function Rectangle(height, width) {
var self = this;
self.height = height;
self.width = width;
self.calcArea = (function() {
console.log(this.height);
return self.height * self.width;
})();
}
var newone = new Rectangle(12,24)
console.log(newone.calcArea);
Its working just fine. You are not using the returned value.
function Rectangle(height, width) {
this.height = height;
this.width = width;
this.calcArea = function() {
console.log(this.height);
return this.height * this.width;
};
var area =this.calcArea(); // trying to do it here, its invoked but no result
console.log(area); //288
}
var newone = new Rectangle(12,24);
Related
If I create a custom class in Google Apps Script and assign it to a variable, can I create a server side onchange event that will react when the values change? For example, something like:
var Polygon = function(height, width) {
this.height = height;
this.width = width;
this.save = function() { <code to draw the polygon here ...> };
}
Polygon.onchange = function() {
currentPolygon = this.value;
currentPolygon.draw();
}
var myPolygon = new Polygon(10, 12);
myPolygon.height = 20; // triggers draw
Or, must it be included in a set function? For example:
var Polygon = function(height, width) {
var myHeight = height;
var myWidth = width;
this.height = function() { return myHeight; }
this.width = function() { return myWidth; }
this.draw = function() { <code to draw the polygon here ...> };
this.changeHeight = function(value) {
myHeight = value;
this.draw();
}
this.changeWidth = function(value) {
myWidth = value;
this.draw();
}
}
var myPolygon = new Polygon(10, 12);
myPolygon.changeHeight(20);
There is no such handler. But you can use a proxy to intercept all set calls:
/*<ignore>*/console.config({maximize:true,timeStamps:false,autoScroll:false});/*</ignore>*/
const Polygon = function(height, width) {
this.height = height;
this.width = width;
this.drawn = 0;
this.draw = function() {
this.drawn += 1;
};
};
const PolygonOnchangeHandler = {
set(target, prop, value) {
Reflect.set(target, prop, value);//calls set
Reflect.apply(target.draw, target, []);//calls draw
},
};
const myPolygon = new Proxy(new Polygon(10, 12), PolygonOnchangeHandler);
myPolygon.height = 20; // trigges draw
console.log(myPolygon.drawn);//drawn once
myPolygon.width = 5;
console.log(myPolygon.drawn);//drawn twice
<!-- https://meta.stackoverflow.com/a/375985/ --> <script src="https://gh-canon.github.io/stack-snippet-console/console.min.js"></script>
I would like to make the getArea() function a prototype, and am not sure if this ES6 (?) format is automatically doing this for me, or do I still need to declare a prototype in a separate Object.prototype.method = function() {}
construct?
class Polygon {
constructor(height, width) {
this.height = height;
this.width = width;
}
getArea() {
return this.height * this.width;
}
}
It is.
The ES6 class format basically translates to something like this:
function Polygon(height, width) {
this.height = height;
this.width = width;
}
Polygon.prototype.getArea = function() {
return this.height * this.width;
};
What is confusing is how this simple script works fine:
function A() {
this.value = 0;
}
A.prototype.foo = function() {
console.log(this.value);
};
function B() {
this.value = 1;
this.foo();
}
B.prototype = Object.create(A.prototype);
B.prototype.bar = function() {
console.log(this instanceof A);
}
new B().bar();
// outputs 1, true
However, this larger script gives an error this.loadDimensions is not a function:
Basically, there is a Player class, which inherits from a MovingComponent class, which inherits from a VisibleComponent class. They all have methods attached to them.
const PX_SZ = 4, MAX_HEIGHT = 100, MIN_HEIGHT = 300;
var resources = {};
resources.sprites = {};
resources.sprites.player = new Image();
resources.sprites.player.src = "resources/sprites/player.png";
resources.sprites['default'] = new Image();
resources.sprites['default'].src = "resources/sprites/default.png";
resources.sprites.items = {};
resources.sprites.backgroundEntities = {};
var itemsTemp = ['default', 'coin0'];
for (var i=0; i<itemsTemp.length; i++) {
var item = itemsTemp[i];
resources.sprites.items[item] = new Image();
resources.sprites.items[item].src = "resources/sprites/items/" + item + ".png";
}
var backgroundEntitiesTemp = ['tree0'];
for (var i=0; i<backgroundEntitiesTemp.length; i++) {
var ent = backgroundEntitiesTemp[i];
resources.sprites.backgroundEntities[ent] = new Image();
resources.sprites.backgroundEntities[ent].src = "resources/sprites/background-entities/" + ent + ".png";
}
var canvas, ctx;
var player = new Player();
var keys = {};
var game = new Game(Math.floor(Math.random()*1000000));
var world = new World();
/** #class */
function Game(seed) {
this.seed = seed;
}
/** #class */
function World() {
this.gravity = 0.4;
this.chances = {
items: {
coin0: 0.005
},
backgroundEntities: {
tree0: 0.05
}
};
this.itemsFloating = [];
this.backgroundEntities = [];
// for spawning
this.exploredRightBound = 0;
this.exploredLeftBound = 0;
}
World.prototype.generate = function(left, right) {
if (left >= right) throw "left >= right in World#generate(left,right)";
for (x = left; x < right; x += PX_SZ) {
// world generation code here
// coin0
var level = getGroundHeightAt(x)
if (Math.random() <= this.chances.items.coin0) {
var item = new ItemFloating("coin0", x, level-20);
this.itemsFloating.push(item);
}
if (Math.random() <= this.chances.backgroundEntities.tree0) {
var ent = new BackgroundEntity("tree0", x, level-resources.sprites.backgroundEntities.tree0.height);
this.backgroundEntities.push(ent);
}
}
};
/**
* #class
* anything that has a sprite attached to it
*/
function VisibleComponent() {
this.sprite = resources.sprites['default'];
}
VisibleComponent.prototype.loadDimensions = function() {
console.log('load');
};
VisibleComponent.prototype.draw = function() {
ctx.drawImage(this.sprite, this.x, this.y, this.width, this.height);
};
/** #class */
function Item(name="default") {
VisibleComponent.call(this);
this.name = name || "default";
this.sprite = resources.sprites.items[name];
this.loadDimensions();
}
Item.prototype = Object.create(VisibleComponent.prototype);
/** #class */
function ItemFloating(name, x, y) {
Item.call(this, name);
this.name = name;
this.x = x;
this.y = y;
this.loadDimensions(); // (when ready of now)
}
ItemFloating.prototype = Object.create(Item.prototype);
/** #class */
function BackgroundEntity(name="default", x=0, y=0) {
VisibleComponent.call(this);
this.name = name;
this.x = x;
this.y = y;
this.width = 1;
this.height = 1;
this.sprite = resources.sprites.backgroundEntities[this.name];
this.loadDimensions();
}
BackgroundEntity.prototype = Object.create(VisibleComponent.prototype);
/** #class */
function MovingEntity(x=0, y=0) {
VisibleComponent.call(this);
this.x = x;
this.y = y;
this.width = 1;
this.height = 1;
}
MovingEntity.prototype = Object.create(VisibleComponent.prototype);
MovingEntity.prototype.collisionWith = function(ent) {
return ((this.x>=ent.x&&this.x<=ent.x+ent.width) || (ent.x>=this.x&&ent.x<=this.x+this.width))
&& ((this.y>=ent.y&&this.y<=ent.y+ent.height) || (ent.y>=this.y&&ent.y<=this.y+this.height));
};
/** #class */
function Player() {
MovingEntity.call(this);
this.inventory = {};
console.log(this instanceof VisibleComponent);
this.speed = 4;
this.jumpSpeed = 8;
this.vspeed = 0;
this.sprite = resources.sprites.player;
this.loadDimensions();
this.direction = "right";
}
Player.prototype = Object.create(MovingEntity.prototype);
Player.prototype.draw = function() {
ctx.save();
ctx.translate(this.x, this.y);
if (this.direction == "left") ctx.scale(-1, 1); // flip over y-axis
ctx.translate(-this.sprite.width, 0);
ctx.drawImage(this.sprite, 0, 0, this.width, this.height);
ctx.restore();
}
Player.prototype.move = function() {
if (keys['ArrowLeft']) {
this.x -= this.speed;
this.direction = "left";
var leftEdge = this.x-canvas.width/2-this.width/2;
if (leftEdge < world.exploredLeftBound) {
world.generate(leftEdge, world.exploredLeftBound);
world.exploredLeftBound = leftEdge;
}
}
if (keys['ArrowRight']) {
this.x += this.speed;
this.direction = "right";
var rightEdge = this.x+canvas.width/2+this.width/2;
if (rightEdge > world.exploredRightBound) {
world.generate(world.exploredRightBound, rightEdge);
world.exploredRightBound = rightEdge;
}
}
var level = getGroundHeightAt(this.x+this.width/2);
if (this.y + this.height < level) {
this.vspeed -= world.gravity;
} else if (this.y + this.height > level) {
this.y = level - this.height;
this.vspeed = 0;
}
if (keys[' '] && this.y+this.height == getGroundHeightAt(this.x+this.width/2)) this.vspeed += this.jumpSpeed;
this.y -= this.vspeed;
for (var i=0; i<world.itemsFloating.length; i++) {
var item = world.itemsFloating[i];
if (this.collisionWith(item)) {
if (this.inventory.hasOwnProperty(item.name)) this.inventory[item.name]++;
else this.inventory[item.name] = 1;
world.itemsFloating.splice(i, 1);
}
}
};
I'm fairly new to javascript inheritance, so I don't understand what I'm doing wrong. Also, since the first script worked, I figured there's something I'm just overlooking in my second script. Any help would be appreciated.
EDIT
In the beginning of the file, I declare player as a new Player(). resources contains Image instances that point to various image files. ctx and canvas are pretty self-explanatory globals.
Also, player isn't recognized as an instance of MovingEntity or VisibleComponent, even though Player's prototype is set to Object.create(MovingEntity.prototype), which has its prototype set to Object.create(VisibleComponent.prototype).
One other thing to mention is that in the definition of loadDimensions() in VisibleComponent, either the onload property of this.sprite is set to a function, or addEventListener() is called for 'load', depending on whether this.sprite has loaded (width != 0) or not.
In the beginning of the file, I declare player as a new Player().
That's the problem, you need to call the constructor after having set up your class. It currently doesn't throw an error about Player not being a function because the declaration is hoisted, but the prototype is not yet initialised with the value you expect so it indeed does not have a .loadDimensions() method yet.
The output of console.log() is the value of width when I use the getRatioValue() function to multiply the inserted value of height and the calculated ratio. I cannot find why this is happening.
var soFunction = function(args) {
this.width = args.width || 0;
this.height = args.height || 0;
this.getRatioValue = function(value) {
var ratio = this.width / this.height;
return value * ratio;
};
console.log(this.getRatioValue(this.height)); // returns 1200
}
// Initialize object
var test = new soFunction({width: 1200, height: 980});
It is an simple mathematic operation. If you will write your function on one line you will get.
value is equal to height, so
return this.height * this.width / this.height
this.height's destroy each other and it returns the this.width
I've hit a wall trying to figure this out. I'm new to OO Javascript and trying to put together my first class/object. I'm trying to create a canvas loader and it's not working. I've narrowed down the error to the requestAnimationWindow portion inside my animate function in my clock class. I get a Object [object global] has no method 'animate' error. Here is my code.
HTML:
<div id="loader"><canvas id="showLoader" width="250" height="250"></canvas><div id="showTimer"><p id="elapsedTime">
<script>
var clockTest = new clock(document.getElementById("showLoader"), 0, 100);
clockTest.animate();
</script>
</p></div></div>
Javascript:
function clock(canvas, curPerc, endPrecent){
var showPerc = document.getElementById("elapsedTime");
this.canvas = document.getElementById("showLoader");
var context = this.canvas.getContext('2d');
var x = this.canvas.width / 2;
var y = this.canvas.height / 2;
var radius = 75;
this.curPerc = 0;
this.endPercent = 110;
var counterClockwise = false;
var circ = Math.PI * 2;
var quart = Math.PI / 2;
context.lineWidth = 10;
context.strokeStyle = '#ed3f36';
this.animate = function(current) {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.context.beginPath();
this.context.arc(x, y, radius, -(quart), ((circ) * current) - quart, false);
this.context.stroke();
this.curPerc++;
if(this.curPerc < this.endPercent) {
requestAnimationFrame(function () {
this.animate(curPerc / 100);
showPerc.innerHTML = this.curPerc + '%';
});
}
};
}
Any tips is appreciated. Thanks!
It is to do with the context of this in the anonymous function you pass to requestAnimationFrame, its not the this you think. Use a closure
i.e.
this.animate = function(current) {
var self = this; //<-- Create a reference to the this you want
self.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
/.. etc, etc..
if(self.curPerc < self.endPercent) {
requestAnimationFrame(function () {
self.animate(self.curPerc / 100); //<-- and use it here
showPerc.innerHTML = self.curPerc + '%'; //<-- and here
});
}
};
On a couple of other points, I would try to structure the object a bit better, you don't seem to be keeping reference to the properties correctly. The parameters you passed in , are not store on the object, and you are not storing the context correctly. Something like:
function clock(canvas, curPerc, endPrecent) {
var self = this;
// Set object properties here, i.e. from the parameters passed in
// Note also, anything that is a property (i.e. this. ) is public, can be accessed from otuside this object,
// whereas variable declared with var , are privte, can only be access within this object
self.canvas = canvas;
self.curPerc = curPerc;
self.endPercent = endPrecent;
self.context = self.canvas.getContext('2d'); //needs to be store like this, if you want to access below as this.context
self.context.lineWidth = 10;
self.context.strokeStyle = '#ed3f36';
//Private variables
var showPerc = document.getElementById("elapsedTime");
var x = self.canvas.width / 2;
var y = self.canvas.height / 2;
var radius = 75;
var counterClockwise = false;
var circ = Math.PI * 2;
var quart = Math.PI / 2;
//Methods
self.animate = function (current) {
self.context.clearRect(0, 0, self.canvas.width, self.canvas.height);
self.context.beginPath();
self.context.arc(x, y, radius, -(quart), ((circ) * current) - quart, false);
self.context.stroke();
self.curPerc++;
if (self.curPerc < self.endPercent) {
requestAnimationFrame(function () {
self.animate(curPerc / 100);
showPerc.innerHTML = self.curPerc + '%';
});
}
};
}
is starting to head in a better direction.
I ran into the same problem using three.js while calling requestAnimationFrame from inside an ES6 class method and the way I ended up solving it was by:
animate() {
requestAnimationFrame(() => this.animate());
this.render();
}