JavaScript Class and Custom event handler proper syntax - javascript

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.

Related

oop - How to get this of this 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...}

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();

JavaScript - Create New Object (Function) Using an object literal?

I have a function defined in my JavaScript with two properties: size and url:
function IconInfo() {
this.size = size;
this.url = url;
}
Inside another function I create a new instance of IconInfo and set the properties this way:
function SomeFunction() {
var icon = new IconInfo();
icon.size = 64;
icon.url = "http://somesite.com/image1.png";
}
Isn't there a way to create an instance like the following:
var icon = new IconInfo({
size : 64,
url : "http://somesite.com/image1.png"
});
I like that notation better, it seems more concise and easier to read.
===========================UPDATE================================
Based on Tim's answer of doing it the following way:
var icon = {
size : 64,
url : "http://somesite.com/image1.png"
};
I have the following question.
Let's say I have 2 functions both with the same exact properties and constructor arguments:
function Func1(x) {
this.x = x;
}
function Func2(x) {
this.x = x;
}
And I have a container that holds either type:
function Container() {
this.f = null;
}
If I add one of the functions using that method what type will it actually be?
...
var container = new Container();
container.x = new f { // is f Func1 or Func2?
x: 10;
}
There are a lot of options, but keeping it simple.. you could just do this:
var icon = {
size : 64,
url : "http://somesite.com/image1.png"
};
You don't have to declare a "type", but you could.. sort of like this:
function IconInfo(size, url)
this.size = size;
this.url = url;
);
var icon = new IconInfo(2, "www.google.com");
You can actually make it even more concise. First, you're forgetting to set the variables inside IconInfo; it should be this:
function IconInfo(size, url) {
this.size = size;
this.url = url;
}
To create a new IconInfo in your other function, you'll just need to call it like this:
function SomeFunction() {
var icon = new IconInfo(64, "http://somesite.com/image1.png");
}

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.

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