Auto increment value in javascript class - javascript

I'm trying to auto increment a properties value each time I instantiate a new instance of the class. This is what my class constructor looks (I abstracted it down just a bit):
var Playlist = function(player, args){
var that = this;
this.id = ?; //Should auto increment
this.tracks = [];
this.ready = false;
this.unloaded = args.length;
this.callback = undefined;
this.onready = function(c){
that.callback = c;
};
this.add = function(tracks){
for(var i = 0; i < tracks.length; i++){
this.tracks.push(tracks[i]);
this.resolve(i);
}
};
this.resolve = function(i){
SC.resolve(that.tracks[i]).then(function(data){
that.tracks[i] = data;
if(that.unloaded > 0){
that.unloaded--;
if(that.unloaded === 0){
that.ready = true;
that.callback();
}
}
});
};
player.playlists.push(this);
return this.add(args);
};
var playlist1 = new Playlist(player, [url1,url2...]); //Should be ID 0
var playlist2 = new Playlist(player, [url1,url2...]); //Should be ID 1
I'd like to not define an initial variable that I increment in the global scope. Could anyone hint me in the right direction? Cheers!

You can use an IIFE to create a private variable that you can increment.
var Playlist = (function() {
var nextID = 0;
return function(player, args) {
this.id = nextID++;
...
};
})();

You can set Playlist.id = 0 somewhere in your code, then increment it in the constructor and assign the new value to the instance property, as: this.id = Playlist.id++.
This suffers from the fact that it is not well encapsulated, so it could be misused.
Otherwise, I was to propose the solution described by Mike C, but he already set a good answer that contains such an idea, so...

Related

Dynamic Javascript Variable?

I am not very sure how to name the question. What i am trying to achieve is this..
I have a set of Global Variable, they will need to be replicated over and over, but assigned with different set's name example. For example
var start
var end
var time
And i have many set/model that i have to create and change, so i am wondering if it is possible to create 1 set and i just have a var modelnumber which then i can just copy and paste them and change the modelnumber so i wont have to change thousands of variable names?
Example
var modelnumber = "1";
var modelstart = modelnumber + "modelstart";
var modelend = modelnumber + "modelend";
var modeltime = modelnumber + "modeltime";
Edit: To provide more info
So i have model1.js , model2.js model3.js and so on....and all the variable names function names are the same, and to save me time, i want to write 1 set of code that i can just change the var modelname at the top of each field so i wont have to change the thousands of variable names and function names..
You can always write a function:
function createVariables(modelNumber) {
window[modelNumber + 'modelstart'] = 1;
window[modelNumber + 'modelend'] = 2;
window[modelNumber = 'modeltime'] = 3;
}
createVariables(1);
Or change it to however you want. :)
UPDATE: (use global in place of window for NodeJS).
I think you're looking for a normal object literal. You can specify the property keys of the object with strings, which will give you the dynamic effect you're looking for.
Here's an example, using a for loop to populate the object.
var models = {};
var number_of_keys = 1000;
for(var i = 1; i < number_of_keys; i++) {
var keyName = 'model' + i;
var model = {
'start': i + 'modelstart',
'end': i + 'modelend',
'time': i + 'modeltime'
}
models[keyName] = model;
}
console.log(models);
Update:
As an example of how you could access your populated models, consider the following:
// You can effectively replace the `1` in this example with any number:
var model1 = models['model1'];
// model1 would be:
// {
// 'start': '1modelstart',
// 'end' : '1modelend',
// 'time': '1modeltime'
// }
var start1 = model1.start;
var end1 = model1.end;
var time1 = model1.time;
// Pseudo-code
var modelN = models['modelN'];
var startN = modelN.start;
var endN = modelN.end;
var timeN = modelN.time;
HTH
You could (should?) use an object or an array of objects.
For example:
// The "Model"
var Model = function(start,end,time) {
this.start = start;
this.end = end;
this.time = time;
}
// One option.
// Assign "Model" to the models
var models = {
'm1': new Model(x,y,z),
'm2': new Model(a,b,c)
}
// Access values
if (models.m1) {
alert("m1 end:["+ models.m1.end +"]");
}
// Add a "new" model
models['ace'] = new Model(r,s,t);
// or even
models.club = new Model(e,f,g);
You could also extend it like so:
Model.prototype.debug = function(id) {
if (id) {
console.log("model id:["+ id +"]");
}
console.log("start:["+ this.start +"]");
console.log("end:["+ this.end +"]");
console.log("time:["+ this.time +"]");
}
Which you would call like so:
models.m1.debug();
Or even:
for(x in models) {
models[x].debug(x);
}
Here is a code snippet example.
var Model = function(start,end,time) {
this.start = start;
this.end = end;
this.time = time;
}
Model.prototype.debug = function(id) {
if (id) {
console.log("model id:["+ id +"]");
}
console.log("start:["+ this.start +"]");
console.log("end:["+ this.end +"]");
console.log("time:["+ this.time +"]");
}
var models = {
'm1' : new Model('x','y','z'),
'm2' : new Model('a','b','c')
};
models.ace = new Model('r','s','t');
for(x in models) {
models[x].debug(x);
}

AS3: How do I get dynamic loader URL from LoaderInfo in Event Listener Function?

I'm loading many pictures, and am using an array to do so.
loader[i].load(new URLRequest(picture[i]));
My Event Listener function is enabled like this:
loader[i].contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete);
My onComplete event handler shows this:
trace(e.target); //OUTPUT: [object LoaderInfo]
I've looked for properties in LoaderInfo that might identify which loader initiated the listener (the value of "i") so that I can putz around with each one specifically, like this:
bitmapDataArr[i] = e.target.content.bitmapData;
bmVisArr[i] = new Bitmap(bitmapDataArr[i]);
But cannot determine which "i" initiated the specific instance of the listener.
Any ideas? I tried giving a name to LoaderInfo to no avail. I still can't extract the pesky little identifying number.
EDIT showing loop for loaders and onComplete function:
for (i = 0; i < 10; i++) {
loader[i] = new Loader();
loader[i].contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete, false, 0, true);
loader[i].load(new URLRequest(letter[i]));
}
private function onComplete(e:Event):void {
trace("e.target",e.target); //OUTPUT: e.target [object LoaderInfo]
var LI:LoaderInfo = e.target as LoaderInfo;
var eNum:int = (????);
bitmapDataArr[eNum] = e.target.content.bitmapData;
bmVisArr[eNum] = new Bitmap(bitmapDataArr[eNum]);
}
You'll somehow need to bring i value to onComplete function. For example, in the this context or thru an argument.
P.S.: It's easier to use weak ref. Dictionaries instead of deleting properties, though I don't know much about AS3.
Here's an example that also shows how to remove the event listeners (including their callback functions):
/* An object containing callback
* functions used along with event listeners.
*/
const callbacks: Object = {};
/* This function will re-declare and hoist i
* in itself. */
private function loop(i: uint): void {
loader[i] = new Loader;
const wrapped =
callbacks[i] = function wrapper(...args) {
// Pass all arguments (respectively event and i)
onComplete.apply(null, args);
// Function#apply(thisContext, arguments)
// Rest exp. isn't implemented yet, else we could just do:
// onComplete(...args);
};
loader[i].contentLoaderInfo
.addEventListener(Event.COMPLETE, wrapped, false,
0, true);
loader[i].load(new URLRequest(letter[i]));
};
for (var i: uint = 0; i < 10; ++i) loop(i);
private function onComplete(e: Event, i: uint): void {
const loaderInfo: LoaderInfo = e.target as LoaderInfo;
bitmapDataArr[i] = e.target
.content.bitmapData;
bmVisArr[i] = new Bitmap(bitmapDataArr[i]);
loader[i].contentLoaderInfo
.removeEventListener(
Event.COMPLETE, callbacks[i]
);
// Deletes the property that stores
// the function inside callbacks
delete callbacks[i];
}
Since posting this question, I've been utilizing the following class. It takes in an integer (number pictures to load) and gives public access to an array of Sprites in the Array "ShapeArr."
Each sprite's name property is derived from its URL name. (name:"pic1" from loaded url "assets/pic1.png")
I was having trouble with the whole concept/implementation of inline functions, and have been using this approach instead.
package {
import flash.display.BitmapData;
import flash.display.Bitmap;
import flash.display.Loader;
import flash.net.URLRequest;
import flash.display.Sprite;
import flash.events.Event;
public class MultipleImageLoader extends Sprite {
{private var pic:Array = [
"assets/pic1.png", "assets/pic2.png", "assets/pic3.png", "assets/pic4.png",
]}
private var loader:Array = [];
public var ShapeArr:Array = [];
public var bitmapDataArr:Array = [];
public var bmVisArr:Array = [];
private var shapeText:Array = [];
private var picArray:Array = [];
private var count:int = 0;
private var loaderCounter:int = 0;
private var numPicsToLoad:int;
private var a:String;
public var loaded:Boolean = false;
public function MultipleImageLoader(numPics:int):void {
numPicsToLoad = numPics;
loaded = false;
init();
}
private function init(e:Event = null):void {
if (hasEventListener(Event.ADDED_TO_STAGE)) {
removeEventListener(Event.ADDED_TO_STAGE, init);
}
picArray = new Array;
for (var i:int = 0; i < numPicsToLoad; i++) {
picArray.push(i);
}
initiateLoaders();
}
private function initiateLoaders():void{
loader[loaderCounter] = new Loader;
loader[loaderCounter].contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete, false, 0, true);
a = pic[picArray[loaderCounter]];
//trace("shapecolor load:", a);
shapeText[loaderCounter] = (a.substr(16, a.length - 20));
loader[loaderCounter].load(new URLRequest(a ) );
}
private function onComplete(e:Event):void {
//trace("sssssssssssssssssssssssssshapecolor");
bitmapDataArr[loaderCounter] = e.target.content.bitmapData;
bmVisArr[loaderCounter] = new Bitmap(bitmapDataArr[loaderCounter]);
bmVisArr[loaderCounter].scaleX = .1;
bmVisArr[loaderCounter].scaleY = .1;
bmVisArr[loaderCounter].x =-bmVisArr[loaderCounter].width / 2;
bmVisArr[loaderCounter].y =-bmVisArr[loaderCounter].height / 2;
ShapeArr[loaderCounter] = new Sprite();
ShapeArr[loaderCounter].name = a.substr(7,4);
trace("Name",loaderCounter,ShapeArr[loaderCounter].name );
ShapeArr[loaderCounter].addChild(bmVisArr[loaderCounter]);
loader[loaderCounter].contentLoaderInfo.removeEventListener(Event.COMPLETE, onComplete);
if (loaderCounter <numPicsToLoad-1) {
loaderCounter += 1;
initiateLoaders();
}
//trace("gonna count",count);
counting();
count += 1;
}
private function counting():void {
trace("tile count", count,numPicsToLoad);
if (count < numPicsToLoad-1) {
return;
}
else{
removeEventListener(Event.ENTER_FRAME, counting);
loaded = true;
count = 0;
trace("All Images LOADED");
}
}
}//end Class
}//end Package

how to access object property in javascript

function test(results) {
//Populate the ComboBox with unique values
var Gov;
var values = [];
var features = results.features;
var og;
for (i = 0; i < features.length; i++) {
var aGOV = {
"GovName": features[i].attributes.ENG_NAME,
"GovNO": features[i].attributes.GOV_NO,
"Shape": features[i].geometry
}
og = new Option(features[i].attributes.ENG_NAME, aGOV);
var cbx = document.getElementById("cbxGov");
cbx.options.add(og);
}
}
function gov_selection_change()
{
var cbx = document.getElementById("cbxGov");
var itm = cbx.options[cbx.selectedIndex].value.hasOwnProperty("Shape");
}
html code
<select id="cbxGov" onchange="gov_selection_change()">
My problem is i m not able to access property of aGOV in my gov_selection_change() function, it shows it has no such property, itm is false.
The value property of an HTMLOptionElement always returns a DOMString (a.k.a. text), not an object.
So you have to save what you want to access in a lookup dictionary and then use the returned value as a lookup key.
var lookupDictionary = {};
function test(results) {
var lookupKey,
og;
//...
// donĀ“t get the element in the loop
var cbx = document.getElementById("cbxGov");
//...
for (i = 0; i < features.length; i++) {
lookupKey = features[i].attributes.GOV_NO;
lookupDictionary[lookupKey] = {
"GovName": features[i].attributes.ENG_NAME,
"GovNO": features[i].attributes.GOV_NO,
"Shape": features[i].geometry
}
og = new Option(features[i].attributes.ENG_NAME, lookupKey );
cbx.options.add( og );
}
}
function gov_selection_change() {
var cbx = document.getElementById("cbxGov");
var key = cbx.options[cbx.selectedIndex].value;
var itm = lookupDictionary[key].hasOwnProperty("Shape");
}
The variable aGOV is only available in the context of your result() function. If you want to use it from a different function declare it as a global variable.
Example:
var aGOV;
function result()
{
// initialize aGOV
}
function gov_selection_change()
{
// now you can access the aGOV variable
}

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 "private" vs. instance properties

I'm doing some Javascript R&D and, while I've read Javascript: The Definitive Guide and Javascript Object Oriented Programming, I'm still having minor issues getting my head out of class based OOP and into lexical, object based OOP.
I love modules. Namespaces, subclasses and interfaces. w00t. Here's what I'm playing with:
var Classes = {
_proto : {
whatAreYou : function(){
return this.name;
}
},
Globe : function(){
this.name = "Globe"
},
Umbrella : new function(){
this.name = "Umbrella"
}(),
Igloo : function(){
function Igloo(madeOf){
this.name = "Igloo"
_material = madeOf;
}
// Igloo specific
Igloo.prototype = {
getMaterial : function(){
return _material;
}
}
// the rest
for(var p in Classes._proto){
Igloo.prototype[p] = Classes._proto[p]
}
return new Igloo(arguments[0]);
},
House : function(){
function House(){
this.name = "My House"
}
House.prototype = Classes._proto
return new House()
}
}
Classes.Globe.prototype = Classes._proto
Classes.Umbrella.prototype = Classes._proto
$(document).ready(function(){
var globe, umb, igloo, house;
globe = new Classes.Globe();
umb = Classes.Umbrella;
igloo = new Classes.Igloo("Ice");
house = new Classes.House();
var objects = [globe, umb, igloo, house]
for(var i = 0, len = objects.length; i < len; i++){
var me = objects[i];
if("whatAreYou" in me){
console.log(me.whatAreYou())
}else{
console.warn("unavailable")
}
}
})
Im trying to find the best way to modularize my code (and understand prototyping) and separate everything out. Notice Globe is a function that needs to be instantiated with new, Umbrella is a singleton and already declared, Igloo uses something I thought about at work today, and seems to be working as well as I'd hoped, and House is another Iglooesque function for testing.
The output of this is:
Globe
unavailable
Igloo
My House
So far so good. The Globe prototype has to be declared outside the Classes object for syntax reasons, Umbrella can't accept due to it already existing (or instantiated or... dunno the "right" term for this one), and Igloo has some closure that declares it for you.
HOWEVER...
If I were to change it to:
var Classes = {
_proto : {
whatAreYou : function(){
return _name;
}
},
Globe : function(){
_name = "Globe"
},
Umbrella : new function(){
_name = "Umbrella"
}(),
Igloo : function(){
function Igloo(madeOf){
_name = "Igloo"
_material = madeOf;
}
// Igloo specific
Igloo.prototype = {
getMaterial : function(){
return _material;
}
}
// the rest
for(var p in Classes._proto){
Igloo.prototype[p] = Classes._proto[p]
}
return new Igloo(arguments[0]);
},
House : function(){
function House(){
_name = "My House"
}
House.prototype = Classes._proto
return new House()
}
}
Classes.Globe.prototype = Classes._proto
Classes.Umbrella.prototype = Classes._proto
$(document).ready(function(){
var globe, umb, igloo, house;
globe = new Classes.Globe();
umb = Classes.Umbrella;
igloo = new Classes.Igloo("Ice");
house = new Classes.House();
var objects = [globe, umb, igloo, house]
for(var i = 0, len = objects.length; i < len; i++){
var me = objects[i];
if("whatAreYou" in me){
console.log(me.whatAreYou())
}else{
console.warn("unavailable")
}
}
})
and make this.name into _name (the "private" property), it doesn't work, and instead outputs:
My House
unavailable
My House
My House
Would someone be kind enough to explain this one? Obviously _name is being overwritted upon each iteration and not reading the object's property of which it's attached.
This all seems a little too verbose needing this and kinda weird IMO.
Thanks :)
You declare a global variable. It is available from anywhere in your code after declaration of this. Wherever you request to _name(more closely window._name) you will receive every time a global. In your case was replaced _name in each function. Last function is House and there has been set to "My House"
Declaration of "private" (local) variables must be with var statement.
Check this out:
var foo = function( a ) {
_bar = a;
this.showBar = function() {
console.log( _bar );
}
};
var a = new foo(4); // _bar ( ie window._bar) is set to 4
a.showBar(); //4
var b = new foo(1); // _bar is set to 1
a.showBar(); //1
b.showBar(); //1
_bar = 5; // window._bar = 5;
a.showBar();// 5
Should be:
var foo = function( a ) {
var _bar = a;
// _bar is now visibled only from both that function
// and functions that will create or delegate from this function,
this.showBar = function() {
console.log( _bar );
};
this.setBar = function( val ) {
_bar = val;
};
this.delegateShowBar = function() {
return function( ) {
console.log( _bar );
}
}
};
foo.prototype.whatever = function( ){
//Remember - here don't have access to _bar
};
var a = new foo(4);
a.showBar(); //4
_bar // ReferenceError: _bar is not defined :)
var b = new foo(1);
a.showBar(); //4
b.showBar(); //1
delegatedShowBar = a.delegateShowBar();
a.setBar(6);
a.showBar();//6
delegatedShowBar(); // 6
If you remove the key word "this", then the _name is in the "Globe" scope.
Looking at your code.
var globe, umb, igloo, house;
globe = new Classes.Globe();
umb = Classes.Umbrella;
igloo = new Classes.Igloo("Ice");
house = new Classes.House();
At last the house will override the "_name" value in globe scope with the name of "My House".

Categories