Understanding discrepancy in Javascript Design Patterns - javascript

I picked up this book a while ago to help understand how to apply my knowledge of design patterns in Java to web developement. However, I've come across an odd discrepancy in the introduction.
The authors state that the following code defines a class that lets the user create animation objects:
//Anim class
var Anim = function(){ /*code here*/ };
Anim.prototype.start = function(){ /*start code here*/ };
Anim.prototype.stop = function() { /*stop code here */};
//create a new Anim object and call the start method
var newAnim = new Anim();
newAnim.start();
I tried something similar with my app.
//Nucleus class and addLink function
var Nucleus = function(){
this.location=null;
this.links = [];
}
//revamp object and add to link array
Nucleus.prototype.addLink = function(marker){ }
The above class was instanciated as a global variable via
var curSpot = new Nucleus();
However, Firebug threw an error at page load stating that Nucleus is not a constructor, and Javascript button handling functionality was disabled. The problem was solved by changing the Nucleus definition to
function Nucleus(){ /*instance members here*/ }
and now the OO functionality works. Why does the latter example work, but the book's example throws an error?
At the request of Ryan Lynch, more code posted. This is the only code where 1curStop is used. As its a global variable,
//global variables
var curStop = new Nucleus();
function showMarkers(){
//unreleated code here
var tourList = [];
//handles a double click event, adds current marker to list and creates one
//if not null. Otherwise, create a new Nucleus object and add current location
(function(marker){
google.maps.event.addListener(marker, 'dblclick', function(){
if (curStop != null){
tourList.push(curStop);
curStop = null;
} else {
curStop = new Nucleus();
curStop.addLocation(marker);
}
}); //end listener
})(marker);
}
function postTour(){
//need CurStop to be a global variable, since only new double-click events add the
//object to the tourList array. This makes sure that all objects are saved on
//button click
tourList.push(curStop);
}

Related

Calling a nested function inside a class JavaScript

I have a class called "Game" and inside it, there's a property called "inventory" which invokes the "Inventory(id)" function where all the inventory functions are created and housed to clear clutter.
I've created a new object
var Game = new Game(body, mainElement, inventoryItems, statusElement);
(it should be self explanitory, the body is selecting the body tag, main element is my main area, status is a status element, and of course inventoryItems is the <ul> I'm using to append my items into.) - you can check the codepen for a better understanding.
The main code you need to help me
function Game(body, mainElement, inventoryItems, statusElement) {
this.body = body;
this.main = mainElement;
this.inventory = Inventory(inventoryItems);
this.status = statusElement;
}
function Inventory(y) {
this.element = y;
this.create = function(itemName, equippable, sellable) {
this.element.append(function(){
var item = '';
if (itemName.length > 0) {
item += "<li>" + itemName;
if (sellable) {
item += "<label>Sell<input type='checkbox' name='sell' onclick='javacript: confirmSell(this);' /></label>";
}
if (equippable) {
item += "<label>Equip<input type='checkbox' name='equip' onclick='javacript: equip(this);' /></label>";
}
}
return item;
}, this.element);
}
}
var Game = new Game(body, mainElement, inventoryItems, statusElement);
Game.inventory(create("Bronze Knife", true, true));
Game.inventory(create("Ramen Noodles", false, true));
Game.inventory(create("Boots w/da Fur", true, true));
Now, I get funny errors when I try calling the inventory(create(string, bool, bool));
It creates the first item, so at least I know "something" is going "somewhat" correctly, or I could be entirely wrong and deserve to just shut my computer down.
In chrome I'm told the Bronze Knife inventory.create is telling me undefined is not a function.
Any help is GREATLY appreciated
EDIT: link
Game.inventory is object (reference to Inventory object (add new before Inventory)) which contain method create, so you can call this method like this
.....
this.inventory = new Inventory(inventoryItems);
.....
var Game = new Game(body, mainElement, inventoryItems, statusElement);
Game.inventory.create("Bronze Knife", true, true);
Game.inventory.create("Ramen Noodles", false, true);
Game.inventory.create("Boots w/da Fur", true, true);
Demo: http://codepen.io/tarasyuk/pen/yyJGJK
Game is a function, and you're defining a variable named Game.
Try to rename var Game = new Game(...); to var game = new Game(...);
Use new when creating an inventory. Use dot notation to access inventory's methods, as they are properties of the inventory object.
// Relevant changes shown:
function Game(body, mainElement, inventoryItems, statusElement) {
this.inventory = new Inventory(inventoryItems);
}
Game.inventory.create("Bronze Knife", true, true);
In the future, you may want to try running your code through a linter like JSLint. Just turn off the "messy white space" option or add /*jslint white: true */ to the top of your file, and it should give you useful feedback on how to solve this on your own.
I ran your code through JSLint and the first thing I noticed was "'Inventory' was used before it was defined." So I moved Inventory above Game. I ran JSLint again and it reported "Missing 'new'." Finally, I looked down through the report and found "'create' was used before it was defined.". Those two points of data could have hinted you in the right direction.

GUI Layer on limeJS

I'm working with limeJS trying to figure out the best way to put a GUI over limeJS. Here's a better explanation:
I've created two classes, one called 'SceneWithGui' and another called 'GuiOverlay'. My intention is that 'SceneWithGui' inherits from 'lime.Scene', adding a property and two methods:
guiLayer (which is a GuiOverlay object)
setGuiLayer
getGuiLayer
As follows:
//set main namespace
goog.provide('tictacawesome.SceneWithGui');
//get requirements
goog.require('lime.Scene');
goog.require('tictacawesome.GuiOverlay');
tictacawesome.SceneWithGui = function() {
lime.Node.call(this);
this.guiLayer = {};
};
goog.inherits(tictacawesome.SceneWithGui, lime.Scene);
tictacawesome.SceneWithGui.prototype.setGuiLayer = function (domElement){
this.guiLayer = new tictacawesome.GuiOverlay(domElement);
};
tictacawesome.SceneWithGui.prototype.getGuiLayer = function (){
return this.guiLayer;
};
The intention with 'GuiOverlay' is to make it hold the DOM element in a way that when the Scene or the Director get resized, it will too. For it, I just inherited from 'lime.Node', hoping it is something already set on it. My code:
//set main namespace
goog.provide('tictacawesome.GuiOverlay');
//get requirements
goog.require('lime.Node');
tictacawesome.GuiOverlay = function(domElement){
lime.Node.call(this);
this.setRenderer(lime.Renderer.DOM);
this.domElement = domElement;
};
goog.inherits(tictacawesome.GuiOverlay, lime.Node);
And that's basically the idea of it all. For better visualization, here's a sample use:
//set main namespace
goog.provide('tictacawesome.menuscreen');
//get requirements
goog.require('lime.Director');
goog.require('lime.Scene');
goog.require('lime.Layer');
goog.require('lime.Sprite');
goog.require('lime.Label');
goog.require('tictacawesome.SceneWithGui');
tictacawesome.menuscreen = function(guiLayer) {
goog.base(this);
this.setSize(1024,768).setRenderer(lime.Renderer.DOM);
var backLayer = new lime.Layer().setAnchorPoint(0, 0).setSize(1024,768),
uiLayer = new lime.Layer().setAnchorPoint(0, 0).setSize(1024,768),
backSprite = new lime.Sprite().setAnchorPoint(0, 0).setSize(1024,768).setFill('assets/background.png');
backLayer.appendChild(backSprite);
this.appendChild(backLayer);
lime.Label.installFont('Metal', 'assets/metalang.ttf');
var title = new lime.Label().
setText('TicTacAwesome').
setFontColor('#CCCCCC').
setFontSize(50).
setPosition(1024/2, 100).setFontFamily('Metal');
uiLayer.appendChild(title);
this.appendChild(uiLayer);
this.setGuiLayer(guiLayer);
};
goog.inherits(tictacawesome.menuscreen, tictacawesome.SceneWithGui);
It all looks pretty on code, but it just doesn't work (the scene doesn't even get displayed anymore). Here are the errors I'm getting through Chrome console:
Uncaught TypeError: Cannot read property 'style' of undefined
director.js:301
Uncaught TypeError: Cannot read property
'transform_cache_' of undefined
I'm not really experienced in JS, so the only clue I found is that this happens when 'tictacawesome.menuscreen' reaches 'goog.base(this)'.
UPDATE: After some messing with the code, I'm able to pass the previous problems I had (before this edit), and now I stumble on the ones cited above. When it reaches directior.js:301, the line is scene.domElement.style['display']='none';. scene exists, but scene.domElement doesn't. Did I somehow fucked up the domElement with my guiOverlay?
Any ideas? Any obvious flaws on my design? Am I on the right path?
Thanks.
Your SceneWithGui extends lime.Scene but you call lime.Node.call(this); in SceneWithGui.
The code in SceneWithGui should look like this:
//set main namespace
goog.provide('tictacawesome.SceneWithGui');
//get requirements
goog.require('lime.Scene');
goog.require('tictacawesome.GuiOverlay');
tictacawesome.SceneWithGui = function() {
lime.Scene.call(this);//This was lime.Node.call(this);
this.guiLayer = {};
};
goog.inherits(tictacawesome.SceneWithGui, lime.Scene);
tictacawesome.SceneWithGui.prototype.setGuiLayer = function (domElement){
this.guiLayer = new tictacawesome.GuiOverlay(domElement);
};
tictacawesome.SceneWithGui.prototype.getGuiLayer = function (){
return this.guiLayer;
};
Can find it out quite quickly when creating a normal scene with var scene = new lime.Scene() breakpoint at that line and stepping into it.
It'll take you to scene.js and then set a breakpoint at lime.Node.call(this);
Then you would have noticed when creating scene = new tictacawesome.menuscreen(); that that breakpoint never hits.

javascript prototype class, this in jquery click

I made a javascript prototype class.
Inside a method I create an jquery click.
But inside this click I want to execute my build function.
When I try to execute a prototype function inside a jquery click it fails because jquery uses this for something else.
I tried some different things, but I couldnt get it working.
Game.prototype.clicks = function(){
$('.flip').click(function(){
if(cardsPlayed.length < 2) //minder dan 2 kaarten gespeeld
{
$(this).find('.card').addClass('flipped');
cardsPlayed.push($(this).find('.card').attr('arrayKey'));
console.log(cardsPlayed[cardsPlayed.length - 1]);
console.log(playingCards[cardsPlayed[cardsPlayed.length - 1]][0]);
if(cardsPlayed.length == 2)// two cards played
{
if(playingCards[cardsPlayed[0]][0] == playingCards[cardsPlayed[1]][0])
{ // same cards played
console.log('zelfde kaarten');
playingCards[cardsPlayed[0]][0] = 0; //hide card one
playingCards[cardsPlayed[1]][0] = 0; //hide card two
//rebuild the playfield
this.build(); //error here
}
else
{
//differend cards
}
}
}
return false;
}).bind(this);
}
The problem is that you're trying to have this reference the clicked .flip element in $(this).find('.card') as well as the Game object in this.build(). this can't have a dual personality, so one of those references needs to change.
The simplest solution, as already suggested by Licson, is to keep a variable pointing to the Game object in the scope of the click handler. Then, just use this inside the handler for the clicked element (as usual in a jQuery handler) and use self for the Game object.
Game.prototype.clicks = function() {
// Keep a reference to the Game in the scope
var self = this;
$('.flip').click(function() {
if(cardsPlayed.length < 2) //minder dan 2 kaarten gespeeld
{
// Use this to refer to the clicked element
$(this).find('.card').addClass('flipped');
// Stuff goes here...
// Use self to refer to the Game object
self.build();
}
}); // Note: no bind, we let jQuery bind this to the clicked element
};
I think you want something like this:
function class(){
var self = this;
this.build = function(){};
$('#element').click(function(){
self.build();
});
};
If I understand correctly, in modern browsers you can simply use bind:
function MyClass() {
this.foo = 'foo';
$('selector').each(function() {
alert(this.foo); //=> 'foo'
}.bind(this));
}
Otherwise just cache this in a variable, typically self and use that where necessary.

javascript item splice self out of list

If I have an array of objects is there any way possible for the item to splice itself out of the array that contains it?
For example: If a bad guy dies he will splice himself out of the array of active enemies.
I probably sound crazy but that ability would simplify my code dramatically, so I hope for something cool =)
The way you would do it is as follows:
var game_state = { active_enemies: [] };
function Enemy() {
// Various enemy-specific things go here
}
Enemy.prototype.remove = function() {
// NOTE: indexOf is not supported in all browsers (IE < 8 most importantly)
// You will probably either want to use a shim like es5-shim.js
// or a utility belt like Underscore.js
var i = game_state.active_enemies.indexOf(this);
game_state.active_enemies.splice(i, 1);
}
See:
Es5-Shim
Underscore.js
Notta bene: There are a couple of issues here with this manner of handling game state. Make sure you are consistent (i.e. don't have enemies remove themselves from the list of active enemies, but heroes remove enemies from the map). It will also make things more difficult to comprehend as the code gets more complex (your Enemy not only is an in-game enemy, but also a map state manager, but it's probably not the only map state manager. When you want to make changes to how you manage map state, you want to make sure that code is structured in such a way that you only need to change it in one place [preferably]).
Assuming the bad guy knows what list he's in, why not?
BadGuy.prototype.die = function()
{
activeEnemies.splice(activeEnemies.indexOf(this), 1);
}
By the way, for older browsers to use indexOf on Arrays, you'll need to add it manually.
You kind of want to avoid circular references
I would suggest creating an object/class that represents the active enemies list. Create methods on that instance for adding/removing a given item from the list - abstracting the inner workings of the data structure from the outside world. If the active enemies list is global (e.g. there's only one of them), then you can just reference it directly to call the remove function when you die. If it's not global, then you'll have to give each item a reference to the list so it can call the function to remove itself.
You can also use an object and instead of splice, delete the enemy:
var activeEnemies = {};
function Enemy() {
this.id = Enemy.getId(); // function to return unique id
activeEnemies[this.id] = this;
// ....
}
Enemy.getId = (function() {
var count = 0;
return function() {
return 'enemyNumber' + count++;
}
}());
Enemy.prototype.exterminate = function() {
// do tidy up
delete activeEnemies[this.id];
}
Enemy.prototype.showId = function() {
console.log(this.id);
}
Enemy.prototype.showEnemies = function() {
var enemyList = [];
for (var enemy in activeEnemies) {
if (activeEnemies.hasOwnProperty(enemy)) {
enemyList.push(enemy);
}
}
return enemyList.join('\n');
}
var e0 = new Enemy();
var e1 = new Enemy();
console.log( Enemy.prototype.showEnemies() ); // enemyNumber0
// enemyNumber1
e0.exterminate();
console.log( Enemy.prototype.showEnemies() ); // enemyNumber1

Functional object basics. How to go beyond simple containers?

On the upside I'm kinda bright, on the downside I'm wracked with ADD. If I have a simple example, that fits with what I already understand, I get it. I hope someone here can help me get it.
I've got a page that, on an interval, polls a server, processes the data, stores it in an object, and displays it in a div. It is using global variables, and outputing to a div defined in my html. I have to get it into an object so I can create multiple instances, pointed at different servers, and managing their data seperately.
My code is basically structured like this...
HTML...
<div id="server_output" class="data_div"></div>
JavaScript...
// globals
var server_url = "http://some.net/address?client=Some+Client";
var data = new Object();
var since_record_id;
var interval_id;
// window onload
window.onload(){
getRecent();
interval_id = setInterval(function(){
pollForNew();
}, 300000);
}
function getRecent(){
var url = server_url + '&recent=20';
// do stuff that relies on globals
// and literal reference to "server_output" div.
}
function pollForNew(){
var url = server_url + '&since_record_id=' + since_record_id;
// again dealing with globals and "server_output".
}
How would I go about formatting that into an object with the globals defined as attributes, and member functions(?) Preferably one that builds its own output div on creation, and returns a reference to it. So I could do something like...
dataOne = new MyDataDiv('http://address/?client');
dataOne.style.left = "30px";
dataTwo = new MyDataDiv('http://different/?client');
dataTwo.style.left = "500px";
My code is actually much more convoluted than this, but I think if I could understand this, I could apply it to what I've already got. If there is anything I've asked for that just isn't possible please tell me. I intend to figure this out, and will. Just typing out the question has helped my ADD addled mind get a better handle on what I'm actually trying to do.
As always... Any help is help.
Thanks
Skip
UPDATE:
I've already got this...
$("body").prepend("<div>text</div>");
this.test = document.body.firstChild;
this.test.style.backgroundColor = "blue";
That's a div created in code, and a reference that can be returned. Stick it in a function, it works.
UPDATE AGAIN:
I've got draggable popups created and manipulated as objects with one prototype function. Here's the fiddle. That's my first fiddle! The popups are key to my project, and from what I've learned the data functionality will come easy.
This is pretty close:
// globals
var pairs = {
{ div : 'div1', url : 'http://some.net/address?client=Some+Client' } ,
{ div : 'div2', url : 'http://some.net/otheraddress?client=Some+Client' } ,
};
var since_record_id; //?? not sure what this is
var intervals = [];
// window onload
window.onload(){ // I don't think this is gonna work
for(var i; i<pairs.length; i++) {
getRecent(pairs[i]);
intervals.push(setInterval(function(){
pollForNew(map[i]);
}, 300000));
}
}
function getRecent(map){
var url = map.url + '&recent=20';
// do stuff here to retrieve the resource
var content = loadResoucrce(url); // must define this
var elt = document.getElementById(map.div);
elt.innerHTML = content;
}
function pollForNew(map){
var url = map.url + '&since_record_id=' + since_record_id;
var content = loadResoucrce(url); // returns an html fragment
var elt = document.getElementById(map.div);
elt.innerHTML = content;
}
and the html obviously needs two divs:
<div id='div1' class='data_div'></div>
<div id='div2' class='data_div'></div>
Your 'window.onload` - I don't think that's gonna work, but maybe you have it set up correctly and didn't want to bother putting in all the code.
About my suggested code - it defines an array in the global scope, an array of objects. Each object is a map, a dictionary if you like. These are the params for each div. It supplies the div id, and the url stub. If you have other params that vary according to div, put them in the map.
Then, call getRecent() once for each map object. Inside the function you can unwrap the map object and get at its parameters.
You also want to set up that interval within the loop, using the same parameterization. I myself would prefer to use setTimeout(), but that's just me.
You need to supply the loadResource() function that accepts a URL (string) and returns the HTML available at that URL.
This solves the problem of modularity, but it is not "an object" or class-based approach to the problem. I'm not sure why you'd want one with such a simple task. Here's a crack an an object that does what you want:
(function() {
var getRecent = function(url, div){
url = url + '&recent=20';
// do stuff here to retrieve the resource
var content = loadResoucrce(url); // must define this
var elt = document.getElementById(div);
elt.innerHTML = content;
}
var pollForNew = function(url, div){
url = url + '&since_record_id=' + since_record_id;
var content = loadResoucrce(url); // returns an html fragment
var elt = document.getElementById(div);
elt.innerHTML = content;
}
UpdatingDataDiv = function(map) {
if (! (this instanceof arguments.callee) ) {
var error = new Error("you must use new to instantiate this class");
error.source = "UpdatingDataDiv";
throw error;
}
this.url = map.url;
this.div = map.div;
this.interval = map.interval || 30000; // default 30s
var self = this;
getRecent(this.url, this.div);
this.intervalId = setInterval(function(){
pollForNew(self.url, self.div);
}, this.interval);
};
UpdatingDataDiv.prototype.cancel = function() {
if (this.intervalId) {
clearInterval(this.intervalId);
this.intervalId = null;
}
}
})();
var d1= new UpdatingDataDiv('div1','http://some.net/address?client=Some+Client');
var d2= new UpdatingDataDiv('div2','http://some.net/otheraddress?client=Some+Client');
...
d1.cancel();
But there's not a lot you can do with d1 and d2. You can invoke cancel() to stop the updating. I guess you could add more functions to extend its capability.
OK, figured out what I needed. It's pretty straight forward.
First off disregard window.onload, the object is defined as a function and when you instantiate a new object it runs the function. Do your setup in the function.
Second, for global variables that you wish to make local to your object, simply define them as this.variable_name; within the object. Those variables are visible throughout the object, and its member functions.
Third, define your member functions as object.prototype.function = function(){};
Fourth, for my case, the object function should return this; This allows regular program flow to examine the variables of the object using dot notation.
This is the answer I was looking for. It takes my non-functional example code, and repackages it as an object...
function ServerObject(url){
// global to the object
this.server_url = url;
this.data = new Object();
this.since_record_id;
this.interval_id;
// do the onload functions
this.getRecent();
this.interval_id = setInterval(function(){
this.pollForNew();
}, 300000);
// do other stuff to setup the object
return this;
}
// define the getRecent function
ServerObject.prototype.getRecent = function(){
// do getRecent(); stuff
// reference object variables as this.variable;
}
// same for pollForNew();
ServerObject.prototype.pollForNew = function(){
// do pollForNew(); stuff here.
// reference object variables as this.variable;
}
Then in your program flow you do something like...
var server = new ServerObject("http://some.net/address");
server.variable = newValue; // access object variables
I mentioned the ADD in the first post. I'm smart enough to know how complex objects can be, and when I look for examples and explanations they expose certain layers of those complexities that cause my mind to just swim. It is difficult to drill down to the simple rules that get you started on the ground floor. What's the scope of 'this'? Sure I'll figure that out someday, but the simple truth is, you gotta reference 'this'.
Thanks
I wish I had more to offer.
Skip

Categories

Resources