oop - How to get this of this javascript - javascript

I'm changing some code to function more like a class in c# so I don't have to make a new script for each occurrence. I've hit problems with my scope on my constructors, I have this constructor
function Game(canvas) {
this.Polygon = function(size, pointCount){
this.size = size;
this.pointCount = pointCount;
this.corners = [];
this.palette = [];
this.render = function (GameObject) {
this.makePolygon(GameObject, this.size, this.corners);
};
};
this.makePolygon = function(GameObject, size, corners){//other code...}
}
My problem is in this.render, makePolygon is inside the class so this means something different. I have tried using .bind(this); but I can't get it to work.
I'm positive that this has been asked before but none of the answers I found would work for me.

A convention that I have used on different teams is to alias this at the top of javascript functions, to avoid this exact problem.
Example:
this.Polygon = function(size, pointCount){
var my = this;
my.size = size;
my.pointCount = pointCount;
my.corners = [];
my.palette = [];
my.render = function (GameObject) {
my.makePolygon(GameObject, my.size, my.corners);
};
};
this.makePolygon = function(GameObject, size, corners){//other code...}
Another option, depending on where this function lies, is to do it as follows.
// Somewhere at the top of this code snippet
var my = this;
//...
my.Polygon = function(size, pointCount){
my.size = size;
my.pointCount = pointCount;
my.corners = [];
my.palette = [];
my.render = function (GameObject) {
my.makePolygon(GameObject, my.size, my.corners);
};
};
my.makePolygon = function(GameObject, size, corners){//other code...}

Related

How to properly structure class in Node.js

I have a class called TileStreamer that I am currently defining as follows:
function TileStreamer {
};
This class has constants, which I define as follows:
// Tiles are 256 x 256 pixels
TileStreamer.prototype.TILE_SIZE = 256;
// Header size in bytes
TileStreamer.prototype.HEADER_SIZE = 28;
// Various table entry sizes in bytes
TileStreamer.prototype.RESOLUTION_ENTRY_SIZE = 12;
TileStreamer.prototype.TILE_COUNT_SIZE = 4;
TileStreamer.prototype.TILE_ENTRY_SIZE = 12;
// Offsets within header
TileStreamer.prototype.WIDTH_OFFSET = 3;
TileStreamer.prototype.HEIGHT_OFFSET = 4;
TileStreamer.prototype.NUM_TABLES_OFFSET = 7;
TileStreamer.prototype.UNPOPULATED_OFFSET = 12092;
There also other variables. These variables are important because they need to be accessible from other classes. They get their values within the methods of this class. This is what I am unsure of as far as structure. What I'm currently trying is:
TileStreamer.prototype.header;
TileStreamer.prototype.resolutionEntry;
TileStreamer.prototype.resolutionTable;
TileStreamer.prototype.filepath;
TileStreamer.prototype.s3;
TileStreamer.prototype.level;
TileStreamer.prototype.ncols;
TileStreamer.prototype.nrows;
TileStreamer.prototype.nlevels;
TileStreamer.prototype.toffset;
TileStreamer.prototype.tsize;
TileStreamer.prototype.modifiedTime;
TileStreamer.prototype.tile;
TileStreamer.prototype.host;
TileStreamer.prototype.bucket;
This class also has methods such as:
TileStreamer.prototype.Init = function(filepath, index, s3config){
var retval = false;
AWS.config.update({accessKeyId: s3config.access_key, secretAccessKey: s3config.secret_key});
var blc = new BlockLibraryConfigs();
var awsConfig = blc.awsConfig;
AWS.config.update({region: awsConfig.region});
var aws = new AWS.S3();
var params = {
Bucket: s3config.bucket,
Key: s3config.tile_directory + filepath,
Range: 'bytes=0-' + (this.HEADER_SIZE - 1)
};
aws.getObject(params, function(err, data){
if(err == null){
TileStreamer.modifiedTime = data.LastModified;
var header = bufferpack.unpack('<7I', data.Body);
TileStreamer.header = header;
TileStreamer.nlevels = header[TileStreamer.NUM_TABLES_OFFSET];
if(TileStreamer.nlevels == 5){
TileStreamer.level = 0;
TileStreamer.ncols = Math.ceil((header[TileStreamer.WIDTH_OFFSET] * 1.0) / TileStreamer.TILE_SIZE);
TileStreamer.nrows = Math.ceil((header[TileStreamer.HEIGHT_OFFSET] * 1.0) / TileStreamer.TILE_SIZE);
}
}
});
};
The method above should set some of the values of the variables, such as modifiedTime so that I can access it in another class such as:
TileStreamer = require('tilestreamer.js');
var ts = new TileStreamer();
ts.Init(parPath, index, config);
var last_modified = ts.modifiedTime;
Just put any public properties you want to initialise when the object is created, directly in the init function. Here's a small example...
function TileStreamer() {
};
TileStreamer.prototype.Init = function() {
this.modifiedTime = new Date();
};
var ts = new TileStreamer();
ts.Init();
console.log(ts);
jsfiddle example
https://jsfiddle.net/v6muohyk/
To get around the issue you're having with setting the object properties in a callback from an asynchronous function, just create a locally accessible variable to reference the object that you are creating at that time...
TileStreamer.prototype.Init = function() {
var thisTileStreamer = this;
asynchFunction(function(err, data) {
thisTileStreamer.modifiedTime = data.lastModified;
});
};
To take it one step further, if you need to execute some code after the init function has completed, then that will require waiting for the asynchronous function to complete, as well. For that, pass a further parameter to init, that is a function to be executed after all the work is done...
TileStreamer.prototype.Init = function(callback) {
var thisTileStreamer = this;
asynchFunction(function(err, data) {
thisTileStreamer.modifiedTime = data.lastModified;
callback();
});
};
var ts = new TileStreamer();
ts.Init(function() {
// put code here that needs to be executed *after* the init function has completed
alert(ts.modifiedTime);
});

inherit the createjs.Container from easeljs library

I am using Easeljs in my spare time. I tried to inherit the Container from createjs object in easeljs library.
var innovative = {};
innovative.object = {};
innovative.object.Basic = function () {
};
//innovative.object.Basic.prototype = new createjs.Container;
innovative.object.LocalPhoto = function (data) {
};
innovative.object.LocalPhoto.prototype = new innovative.object.Basic;
innovative.object.LocalPhoto.prototype.constructor = innovative.object.LocalPhoto;
I have function in LocalPhoto which will add itself to the stage like this
innovative.object.LocalPhoto.prototype.load = function (stage, event) {
var self = this;
stage.addChild(self);
stage.update();
};
This is how i create LocalPhoto object
var self = this;
for (var i = 0; i < values.length; i++) {
this.download (values[i], function (evt) {
var photo;
photo = new innovative.object.LocalPhoto(evt.target.object);
photo.load(self.stage, evt);
});
}
Problem i am facing is when i add a LocalPhoto into the stage, the rest of the localPhoto within that stage will append the same photo as well.
This is what i mean in steps : 1) insert a image within a container and added to the stage.
2) another image within a new container and added to the stage.
At the same time, the later image also added to the other child container which i have added to the stage.
(function() {
function YourFunc(param) {
this.Container_constructor();
this.param = param;
}
var p = createjs.extend(YourFunc, createjs.Container);
p.draw = function() {
this.Container_draw();
// add custom logic here.
}
window.Button = createjs.promote(Button, "Container");
}());
Or Just
var p = YourFunc.prototype = new createjs.Container();

Creating a simple constructor for a grouping of Web Audio API nodes

I have a batch of Web Audio API nodes that look like the code below. I want to abstract this into a simple constructor but I'm having trouble. I'm not sure what I'm doing wrong. The end result should look something like
function filterTemplate(name,freqVal){
this.name = context.createBiquadFilter();
this.name.type = 5;
this.name.gain.value = null;
this.name.Q.value = 1;
this.name.frequency.value = this.freqVal; // freqVal is here
}
When I call the function:
var filter = new filterTemplate("theName",200); //Uncaught TypeError: Cannot call method 'createBiquadFilter' of undefined
I changed the method to look like this and the error is removed
this.name = function(){return context.createBiquadFilter()};
but then I get another error for the various property values
//Uncaught TypeError: Cannot set property 'value' of undefined
I'm really just confused as to the proper way to create a vanilla constructor using built in browser methods and properties.
I want to abstract the code below into looking something like the code above
filter1 = context.createBiquadFilter();
filter1.type = 5;
filter1.gain.value = null;
filter1.Q.value = 1;
filter1.frequency.value = 80; // Changes
filter2 = context.createBiquadFilter();
filter2.type = 5;
filter2.gain.value = 0;
filter2.Q.value = 1;
filter2.frequency.value = 240; // Changes
filter3 = context.createBiquadFilter();
filter3.type = 5;
filter3.gain.value = 0;
filter3.Q.value = 1;
filter3.frequency.value = 750; // Changes
filter4 = context.createBiquadFilter();
filter4.type = 5;
filter4.gain.value = 0;
filter4.Q.value = 1;
filter4.frequency.value = 2200; // Changes
filter5 = context.createBiquadFilter();
filter5.type = 5;
filter5.gain.value = 0;
filter5.Q.value = 1;
filter5.frequency.value = 6000; // Changes
The builder pattern is very nice for this situation. Especially when you can set a lot of properties.
http://jsfiddle.net/yw8Fm/
You can create a simple FilterTemplate class like this.
function FilterTemplate(builder) {
this.context = builder._context;
this.context.type = builder._type;
this.context.gain.value = builder._gainValue;
this.context.Q.value = builder._qValue;
this.context.frequency.value = builder._frequencyValue;
}
It takes a builder object as constructor argument. Here is the Builder.
FilterTemplate.Builder = function () {
this._context = context.createBiquadFilter();
this._type = 5;
this._gainValue = null;
this._qValue = 1;
this._frequencyValue = 80;
this.context = function (val) {
this._context = val; return this;
};
this.type = function (val) {
this._type = val; return this;
};
this.gainValue = function (val) {
this._gainValue = val; return this;
};
this.qValue = function (val) {
this._qValue = val; return this;
};
this.frequencyValue = function (val) {
this._frequencyValue = val; return this;
};
};
You can further extend this example as you like.
Now you can create FilterTemplates with ease.
var filter1 = new FilterTemplate(
(new FilterTemplate.Builder()).frequencyValue(80)
);
var filter2 = new FilterTemplate(
(new FilterTemplate.Builder()).frequencyValue(80).qValue(2)
);
Your problem is with the scope of your context variable.
var filter = new filterTemplate("theName",200); //Uncaught TypeError: Cannot call method 'createBiquadFilter' of undefined
... means that the context variable isn't available from where you're trying to reach it (which is within the filterTemplate constructor). When you do...
this.name = function(){return context.createBiquadFilter()};
... you're assigning the function to this.name instead, and it won't try to access the context until the function is run, and thus the error is removed. What happens instead is that you don't have a filter in this.name, but rather a function, and a function doesn't have a gain property and therefore you get an error when you try to set this.name.gain.value.
What you should look for is where you define the context, and make sure it's possible to access that variable from within filterTemplate.

JavaScript Class and Custom event handler proper syntax

I understand that there are many different views on the way JavaScript should be written, but I was wondering if the way I am writing it is fine. I don't want to go into the workforce writing code that nobody understands.
The basic premise of this code was to test custom event handlers for objects, was wondering if there is any glaring "YOU SHOULD NOT DO THIS" type thing?
function EventClass() {
var self = this;
var events = {};
var i = 0;
self.fire = function(evt, args) {
for (x in events[evt])
events[evt][x].call(this, args);
}
self.on = function(evt, fn) {
if (events[evt] == null) {
events[evt] = []
}
events[evt].push(fn);
}
};
function Human(x, y) {
var self = this;
self.__proto__ = new EventClass();
var xCoord = 0;
var yCoord = 0;
self.events = {
"MOVEMENT" : "movement"
};
self.init = function(x,y) {
xCoord = x;
yCoord = y;
}
self.draw = function(context) {
context.beginPath();
context.arc(xCoord,yCoord,10,0,Math.PI*2,true);
context.closePath();
context.fill();
}
self.moveLeft = function() {
xCoord -= 5;
self.fire(self.events.MOVEMENT, xCoord);
}
self.init(x,y);
};
function Player(x, y) {
var self = this;
self.__proto__ = new Human();
self.init(x,y);
};
function Canvas(c) {
var self = this;
var canvas;
var context;
var objects = [];
self.init = function(c) {
canvas = c;
context = canvas.getContext("2d");
};
this.redraw = function() {
context.clearRect(0,0,300,300);
for (x in objects) {
objects[x].draw(context);
}
}
this.addObject = function(obj) {
objects.push(obj);
obj.on(obj.events.MOVEMENT, function(coord) {
console.log(coord);
self.redraw();
});
};
self.init(c);
}
var canvas = new Canvas(document.getElementById("canvas"));
var human0 = new Human(75,75);
canvas.addObject(human0);
var human1 = new Human(100,100);
canvas.addObject(human1);
var player = new Player(200,200);
canvas.addObject(player);
canvas.redraw();
A couple of small things -- sort of.
The first one is tiny.
Be consistent with the this and self thing.
The example I'm thinking of here is in your Canvas constructor.
The truth of the matter is that nothing inside of your particular implementation requires self.
Unless you're planning on giving your methods to other objects, or callbacks, self isn't required, there, until you get into writing functions which exist INSIDE of the methods.
And even then, all self will help with is accessing the public properties/methods of that particular instance, and wouldn't help you access events or i or whatever.
Not to say that it's not good to do -- it's just more-useful when you're doing the object-composition thing, rather than object-construction thing (and frequently becomes downright necessary, then).
The second thing was more of a problem.
__proto__ might be reasonably-well supported, if you're talking about FireFox/Chrome, but if you need to support wide swaths of browsers, you're going to make your life much more difficult by doing things that way, rather than adding to the ConstructorFN.prototype object.

Cloned objects and reference inside functions

I am trying to do the following:
var element = {};
element.attrA = 'A';
element.attrB = 1;
element.autoAdvance = function(){
var that = this;
setInterval(function(){
that.attrB++;
},100);
}
element.instance = function(){
var clone = $.extend(true, {}, this);
return clone;
}
Now I can do the following just fine:
var e1 = element.instance();
var e2 = element.instance();
e1.attrA = 'not e2s business';
e2.attrA = 'not e1s attrA';
The trouble starts when I try to use autoAdvance:
e1.autoAdvance();
will start the autoAdvance for all cloned 'instances' of element. I am sure this is probably rather trivial but I just don't know how to refer to the parent object inside my autoAdvance-function in a way that it gets properly cloned and only affects the instance. Thanks!
EDIT:
This is the actual code I am using:
var player = {};
player.sprites = {};
player.sprites.back = ['img/playerback_01.png','img/playerback_02.png','img/playerback_03.png'];
player.sprites.head = ['img/playerhead_01.png','img/playerhead_02.png','img/playerhead_03.png'];
player.back = new Image();
player.back.src = player.sprites.back[0];
player.head = new Image();
player.head.src = player.sprites.head[0];
player.loop = function(){
var that = this;
var loop = setInterval(function(){
//remove the [0] state from the sprite array and add it at [2]
var state = that.sprites.head.shift();
that.sprites.head.push(state);
state = that.sprites.back.shift();
that.sprites.back.push(state);
that.back.src = that.sprites.back[0];
that.head.src = that.sprites.head[0];
}, 100);
}
player.x = 0;
player.y = 0;
player.instance = function(){
var clone = $.extend(true, {}, this);
return clone;
}
I generate two players:
var player1 = player.instance();
var player2 = player.instance();
But what is happening is that when I use:
player1.loop();
The animation for player2 will start to play as well.
I suggest you start using "class" in JavaScript. They are more or less a function.
function Player(){
this.sprites={};
......
}
Player.prototype.loop=function(){
....
}
var player1=new Player();
var player2=new Player();
player1.loop();
player2.loop();// this should work better
It doesn't really answer your question but it's an alternative way to write code in a cleaner and better way.

Categories