javascript - how to get object from array, and use its methods? - javascript

i have a problem using a class methods, after it was inserted into array. when i pull it back i can no longer use it methods.
and i know javascript does not have class, when i say class i mean object -or js equal.
suppose i have the following:
// a simple atomic class
function raw_msg(msg) {
this.msg = msg;
this.print = function () {
console.log(this.msg);
}
}
// and then i have this container for this "atomic" class
// which accept array of unknown object (known to me though..) = in_buffer
// i.e in_buffer is just an array of objects (one type of object)
function buffer(in_buffer) {
this.trans_buffer = null;
if (in_buffer!=null)
this.set_buffer (in_buffer);
this.set_buffer = function (buffer) {
this.trans_buffer = [];
var length = buffer.length,
row, new_raw_msg;
for(var x = 0; x < length; x++) {
row = buffer[x];
this.trans_buffer.push(new raw_msg(row));
}
console.log(this.trans_buffer);
}
this.use_some_raw_msg_method = function () {
var firstrow = this.trans_buffer[0];
firstrow.print(); // here is the problem!!!
//this here where i need help as it yield the error:
//Uncaught TypeError: Object #<Object> has no method 'print'
}
}
// this is how i use it, this code sits in a diffrent yet another class...
// this here im just building fake array
var buffer_in = [];
for (var x=0;x<10;x++)
buffer_in.push ("whatever" + x);
this.trans_buffer = new trans_helper(buffer_in);
this.trans_buffer.use_some_raw_msg_method (); // will yield the error as described
i hope this here, is clear, ask away if you need clarifications.
thanks for your help!
note to future readers - there is no problem in retrieving an object and using its methods.

You had several problems with your code.
Associative array does not have .push() method so the following line failed:
buffer_in.push ("whatever" + x);
To fix this just declare plain array:
var buffer_in = [];
You tried to create instance of function called trans_helper which does not exist. The name is buffer instead, so fix would be:
var trans_buffer = new buffer(buffer_in);
Last but not least, you tried to call function in the "class" when it still did not exist yet. JavaScript does not "compile" functions in advance, when inside function it will go line by line. So in this line in your code:
this.set_buffer (in_buffer);
There was still no function called "set_buffer" in your class. To fix this, place the function declaration above, on top.
Live test case.

Related

javascript array value as variable name , how I get them from function

I'm using array value as variable and then call the function N method, how I get them in function N.
I really want to simulate the Javascript array method, I don't want to use parameters to achieve it. For example,
var p1 = [1,2,3,4,5]; p1.push(6);
function _Array() {
this._this = this;
}
_Array.prototype.show = function () {
this._this.forEach(function(item){alert(item);}) //how to print 1,2,3,4,5
};
var p1 = [1,2,3,4,5];
p1 = new _Array();
//p1._Array.call(p1); //not work
// new _Array().show.call(p1); //not work
// p1.show(); //not work
You have to store that in the instance
function N(arr) {
this._this = arr
}
N.prototype.say = function () {
this._this.forEach(function (item) {
console.log(item)
})
}
p1 = new N([1, 2, 3, 4, 5])
p1.say()
If you are insistent on wanting to write a method that takes the array by reference, you can modify the array prototype like so:
Array.prototype.show = function() {
this.forEach(item => alert(item));
}
However, it is a VERY BAD IDEA to modify the built in object prototypes, as this can cause conflicts with external libraries implementing their own "show" function that is being used differently, or cause incompatibilities with future versions of JS that implements this method.
It would be far more prudent in most situations to pass the array as a parameter, unless you have a very specific reason why you're not doing so. In that case, you should at least prefix the method with some sort of project identifier to minimize the chances of conflicts occurring.

calling a class method from an object that is accessed via an array index

Rookie question I'm sure. Be gentle. (Or not, it won't kill me).
Maybe the answer is already posted and I just haven't recognized it in my search, but here goes:
I have a class that defines a method. I've created a couple instances of that class and saved them in an array. When I attempt to access the instance method, as defined in the class, I get an error "Cannot read property'. If I assign the array indexed object to a variable and make the call, it does not throw an error but also does not seem to work as expected. That is, in my loop it seems to assign the method only for the last iteration. The code may help, embarrassing though it may be:
class Track {
constructor(path,docroot) {
this.path = path;
this.docroot = docroot;
}
urlpath() {
var x = this.docroot.length;
return this.path.substring(x);
};
createWave(id) {
var url = this.urlpath();
var wavesurfer = WaveSurfer.create({
container: '#' + id,
waveColor: 'violet',
progressColor: 'purple'
});
wavesurfer.load(url);
return wavesurfer;
}
}
for (i = 0; i < 2; i++) {
tracks[i] = new Track(audiofiles[i],docroot);
waves[i] = tracks[i].createWave("wave-container-" + i);
var w = waves[i];
var playpausebtn = document.createElement('BUTTON');
playpausebtn.innerHTML = "Play / Pause";
playpausebtn.id = "playpause";
document.getElementById('wave-container-' + i).appendChild(playpausebtn);
document.getElementById('playpause').onclick = function() { w.playPause(); };
}
For those familiar with it, wavesurfer.js defines the playPause method for the wavesurfer object, returned by createWave() in my class method. If I call it with w.playPause as above, it works - but only for one of the two instances. If I forego the var w = waves[i] assignment and simply try to set the onclick action to waves[i].playPause()` I get the error mentioned.
I don't know if this is enough to go on, but would appreciate any guidance or hints.

Container Class in Javascript

Total OOP beginner and doing all this in Javascript, so forgive any blatant dumbness on my part:
I want to keep track of dynamically created objects. I believe my solution to this problem is to use a container class.
Is a container class what I want?
If yes, is my implementation correct?
Branch objects are dynamically generated.
Branches objects contain a Branch objects array.
function Branches() {
function Branch() {
var _id;
_id = Math.round(Math.random()*10);
this.getId = function() {
return _id;
}
}
this.createBranch = function() {
var branch = new Branch;
_branches.push(branch);
}
this.getBranches = function() {
return _branches;
}
this.getBranchIds = function() {
var branch_list = this.getBranches();
var branch_ids = [];
for (var i = 0; i < branch_list.length; i++) {
var branch_id = branch_list[i].getId();
branch_ids.push(branch_id);
}
return branch_ids;
}
var _branches = [];
}
// code test
var test = new Branches;
for (var i = 0; i < 7; i++) {
test.createBranch();
}
console.log("Branch IDs:\n" + test.getBranchIds());
Your code works (yay!) despite a few simple problems (you're not generating unique IDs for each branch, for example). If you are happy with the design you chose, feel free to take the code to Code Review. There you will get tips for improving the code as it currently is.
To answer your first, more conceptual question, what you've written is definitely one way to implement what you want. You've gone with a Factory pattern here. You've written a class, Branches, that provides an interface for creating objects of the class Branch.
createBranch is known as a Factory method; it handles the creation of the new object, and in your case, keeping track of that object in your array. It also returns the new object so the user can interact with the Branch object as necessary.
One thing to consider about your implementation is that Branch is private, visible only to code inside the Branches class. This has a few implications that come to mind:
the only way to create a Branch object is through the createBranch factory
all Branch objects will be tracked because of this
Any properties of the Branch constructor (meaning, anything like Branch.property = value) will not be immediately accessible outside of the Branches class.
This may be what you want. But if there is no reason to hide the Branch constructor or prototype from plain sight, then I'd suggest other design patterns.
A good strategy might be to use those constructor properties. This way, you reduce your code by a lot, and have one fewer class to deal with (but no factory methods):
function Branch() {
var _id = Math.round(Math.random() * 10);
this.getId = function () {
return _id;
};
Branch.branches.push(this);
}
Branch.branches = [];
Branch.getIds = function () {
var ids = [];
for (var i in Branch.branches)
ids.push(Branch.branches[i].getId());
return ids;
};
// test code
for (var i = 0; i < 7; i++) {
new Branch();
}
console.log("Branch IDs:\n" + Branch.getIds());

Passing references in javascript

This is my first SO post. I'm eternally grateful for the information this community has and shares. Thanks.
I'm coming from Flash and I'm not even sure what the right question to ask is. All I can do is lay out my code example and then explain what I am trying to do. I do not fully grasp the terms that I am trying to illustrate here so I feel it is best to omit them.
The code below is incomplete as it only includes the parts that I feel are relevant to my question. Please refer to the comments in my code to see my issue.
EDIT: Full source file here: [link removed] The console.log outputs the issue in question.
<script type="text/javascript">
var a_chests = [];
var chestID = 0;
//I'm creating a plugin to be able to make multiple instances
(function ($) {
$.fn.chestPlugin = function (option) {
//This function creates a master sprite object which many of my sprites will use
//I've simplified the features to get to the heart of my question
var DHTMLSprite = function (params) {
var ident = params.ident,
var that = {
getID: function(){
return ident;
}
};
return that;
};
//ChestSprite inherits DHTMLSprites properties and then adds a few of its own
var chestSprite = function(params) {
var ident = params.ident,
that = DHTMLSprite(params);
that.reveal=function(){
console.log(ident);
};
return that;
};
//Here I create multiple instances of the chests
var treasure = function ( $drawTarget,chests) {
for (i=0;i<chests;i++){
var cs = chestSprite({
ident: "chest"+chestID
})
console.log(cs.reveal())
//This logs "chest0", "chest1", "chest2" as the for loop executes
//This behavior is correct and/or expected!
a_chests[chestID]={id:i,ob:cs};
//I add a reference to the new chestSprite for later
chestID++;
//increment the chestID;
}
console.log(a_chests[1].ob.reveal());
//This always logs "chest2" (the last chest that is created), even though
//the logs in the for loop were correct. It seems it is referencing the
//DHTML object (since the DHTMLSprite function returns that;) and since
//there is no reference to which chest I need, it passes the last one.
//Is there any way I can pass a reference to DHTMLSprite in order to retain
//the reference to the three individual chests that are created?
//Is there another solution altogether? Thanks!!!
};
//The rest of the code.
return this.each(function () {
var $drawTarget = $(this);
treasure($drawTarget,3);
});
};
})(jQuery);
</script>
You forgot to declare `that' as a local variable, so it's being overwritten on each iteration.
var chestSprite = function(params) {
var that;
var animInterval;
...
When you write:
a_chests[chestID]={id:i,ob:cs};
You are assigning the cs object itself, not an instance of this object. If later you modify cs, this will also modify what you stored in the ob property.
I guess what you need is a closure:
for (i=0;i<chests;i++){
(function(){
var cs = chestSprite({ident: "chest"+chestID});
a_chests[chestID]={id:i,ob:cs};
})();
}
This way, each loop creates a different cs object.

Javascript function objects

I edited the question so it would make more sense.
I have a function that needs a couple arguments - let's call it fc(). I am passing that function as an argument through other functions (lets call them fa() and fb()). Each of the functions that fc() passes through add an argument to fc(). How do I pass fc() to each function without having to pass fc()'s arguments separately? Below is how I want it to work.
function fa(fc){
fc.myvar=something
fb(fc)
}
function fb(fc){
fc.myothervar=something
fc()
}
function fc(){
doessomething with myvar and myothervar
}
Below is how I do it now. As I add arguments, it's getting confusing because I have to add them to preceding function(s) as well. fb() and fc() get used elsewhere and I am loosing some flexibility.
function fa(fc){
myvar=something
fb(fc,myvar)
}
function fb(fc,myvar){
myothervar=something
fc(myvar,myothervar)
}
function fc(myvar,myothervar){
doessomething with myvar and myothervar
}
Thanks for your help
Edit 3 - The code
I updated my code using JimmyP's solution. I'd be interested in Jason Bunting's non-hack solution. Remember that each of these functions are also called from other functions and events.
From the HTML page
<input type="text" class="right" dynamicSelect="../selectLists/otherchargetype.aspx,null,calcSalesTax"/>
Set event handlers when section is loaded
function setDynamicSelectElements(oSet) {
/**************************************************************************************
* Sets the event handlers for inputs with dynamic selects
**************************************************************************************/
if (oSet.dynamicSelect) {
var ySelectArgs = oSet.dynamicSelect.split(',');
with (oSet) {
onkeyup = function() { findListItem(this); };
onclick = function() { selectList(ySelectArgs[0], ySelectArgs[1], ySelectArgs[2]) }
}
}
}
onclick event builds list
function selectList(sListName, sQuery, fnFollowing) {
/**************************************************************************************
* Build a dynamic select list and set each of the events for the table elements
**************************************************************************************/
if (fnFollowing) {
fnFollowing = eval(fnFollowing)//sent text function name, eval to a function
configureSelectList.clickEvent = fnFollowing
}
var oDiv = setDiv(sListName, sQuery, 'dynamicSelect', configureSelectList); //create the div in the right place
var oSelected = event.srcElement;
if (oSelected.value) findListItem(oSelected)//highlight the selected item
}
Create the list
function setDiv(sPageName, sQuery, sClassName, fnBeforeAppend) {
/**************************************************************************************
* Creates a div and places a page in it.
**************************************************************************************/
var oSelected = event.srcElement;
var sCursor = oSelected.style.cursor; //remember this for later
var coords = getElementCoords(oSelected);
var iBorder = makeNumeric(getStyle(oSelected, 'border-width'))
var oParent = oSelected.parentNode
if (!oParent.id) oParent.id = sAutoGenIdPrefix + randomNumber()//create an ID
var oDiv = document.getElementById(oParent.id + sWindowIdSuffix)//see if the div already exists
if (!oDiv) {//if not create it and set an id we can use to find it later
oDiv = document.createElement('DIV')
oDiv.id = oParent.id + sWindowIdSuffix//give the child an id so we can reference it later
oSelected.style.cursor = 'wait'//until the thing is loaded
oDiv.className = sClassName
oDiv.style.pixelLeft = coords.x + (iBorder * 2)
oDiv.style.pixelTop = (coords.y + coords.h + (iBorder * 2))
XmlHttpPage(sPageName, oDiv, sQuery)
if (fnBeforeAppend) {
fnBeforeAppend(oDiv)
}
oParent.appendChild(oDiv)
oSelected.style.cursor = ''//until the thing is loaded//once it's loaded, set the cursor back
oDiv.style.cursor = ''
}
return oDiv;
}
Position and size the list
function configureSelectList(oDiv, fnOnClick) {
/**************************************************************************************
* Build a dynamic select list and set each of the events for the table elements
* Created in one place and moved to another so that sizing based on the cell width can
* occur without being affected by stylesheet cascades
**************************************************************************************/
if(!fnOnClick) fnOnClick=configureSelectList.clickEvent
if (!oDiv) oDiv = configureSelectList.Container;
var oTable = getDecendant('TABLE', oDiv)
document.getElementsByTagName('TABLE')[0].rows[0].cells[0].appendChild(oDiv)//append to the doc so we are style free, then move it later
if (oTable) {
for (iRow = 0; iRow < oTable.rows.length; iRow++) {
var oRow = oTable.rows[iRow]
oRow.onmouseover = function() { highlightSelection(this) };
oRow.onmouseout = function() { highlightSelection(this) };
oRow.style.cursor = 'hand';
oRow.onclick = function() { closeSelectList(0); fnOnClick ? fnOnClick() : null };
oRow.cells[0].style.whiteSpace = 'nowrap'
}
} else {
//show some kind of error
}
oDiv.style.width = (oTable.offsetWidth + 20) + "px"; //no horiz scroll bars please
oTable.mouseout = function() { closeSelectList(500) };
if (oDiv.firstChild.offsetHeight < oDiv.offsetHeight) oDiv.style.height = oDiv.firstChild.offsetHeight//make sure the list is not too big for a few of items
}
Okay, so - where to start? :) Here is the partial function to begin with, you will need this (now and in the future, if you spend a lot of time hacking JavaScript):
function partial(func /*, 0..n args */) {
var args = Array.prototype.slice.call(arguments, 1);
return function() {
var allArguments = args.concat(Array.prototype.slice.call(arguments));
return func.apply(this, allArguments);
};
}
I see a lot of things about your code that make me cringe, but since I don't have time to really critique it, and you didn't ask for it, I will suggest the following if you want to rid yourself of the hack you are currently using, and a few other things:
The setDynamicSelectElements() function
In this function, you can change this line:
onclick = function() { selectList(ySelectArgs[0], ySelectArgs[1], ySelectArgs[2]) }
To this:
onclick = function() { selectList.apply(null, ySelectArgs); }
The selectList() function
In this function, you can get rid of this code where you are using eval - don't ever use eval unless you have a good reason to do so, it is very risky (go read up on it):
if (fnFollowing) {
fnFollowing = eval(fnFollowing)
configureSelectList.clickEvent = fnFollowing
}
And use this instead:
if(fnFollowing) {
fnFollowing = window[fnFollowing]; //this will find the function in the global scope
}
Then, change this line:
var oDiv = setDiv(sListName, sQuery, 'dynamicSelect', configureSelectList);
To this:
var oDiv = setDiv(sListName, sQuery, 'dynamicSelect', partial(configureSelectListAlternate, fnFollowing));
Now, in that code I provided, I have "configureSelectListAlternate" - that is a function that is the same as "configureSelectList" but has the parameters in the reverse order - if you can reverse the order of the parameters to "configureSelectList" instead, do that, otherwise here is my version:
function configureSelectListAlternate(fnOnClick, oDiv) {
configureSelectList(oDiv, fnOnClick);
}
The configureSelectList() function
In this function, you can eliminate this line:
if(!fnOnClick) fnOnClick=configureSelectList.clickEvent
That isn't needed any longer. Now, I see something I don't understand:
if (!oDiv) oDiv = configureSelectList.Container;
I didn't see you hook that Container property on in any of the other code. Unless you need this line, you should be able to get rid of it.
The setDiv() function can stay the same.
Not too exciting, but you get the idea - your code really could use some cleanup - are you avoiding the use of a library like jQuery or MochiKit for a good reason? It would make your life a lot easier...
A function's properties are not available as variables in the local scope. You must access them as properties. So, within 'fc' you could access 'myvar' in one of two ways:
// #1
arguments.callee.myvar;
// #2
fc.myvar;
Either's fine...
Try inheritance - by passing your whatever object as an argument, you gain access to whatever variables inside, like:
function Obj (iString) { // Base object
this.string = iString;
}
var myObj = new Obj ("text");
function InheritedObj (objInstance) { // Object with Obj vars
this.subObj = objInstance;
}
var myInheritedObj = new InheritedObj (myObj);
var myVar = myInheritedObj.subObj.string;
document.write (myVar);
subObj will take the form of myObj, so you can access the variables inside.
Maybe you are looking for Partial Function Application, or possibly currying?
Here is a quote from a blog post on the difference:
Where partial application takes a function and from it builds a function which takes fewer arguments, currying builds functions which take multiple arguments by composition of functions which each take a single argument.
If possible, it would help us help you if you could simplify your example and/or provide actual JS code instead of pseudocode.

Categories