I'd like to retrieve an instance of some js Class with only the value of a parameter
lets say :
function myClass(id)
{
this.id = id
}
myClass.prototype.sayHello = function()
{
alert('hello');
}
myClass.instanceFromID = function()
{
...
}
var instance1 = new myClass(22);
var instance2 = new myClass(33);
var instance3 = new myClass(232);
var instance4 = new myClass(1533);
I would love to be able to access the instance by some method like
myClass.instanceFromID(33).sayHello();
I've been thinking of 2 solutions but they seam overkill :
First idea:
storing all the instances in an array, (global scope or static to the class) and iterating over all of them to find and return the instance, but this implies to keep track, add and remove the instances from the array.
Second idea:
make each instance listen to a custom event triggered from the document and compare the data emitted with inner parameter to check if it is concerned and emit a signal back to the document.
How can I achieve this in a simple way or is there no other way than these 2?
Based on what you've written, having the class itself keep track of instances with an instance variable seems to be the approach you're looking for. Of course, this means that instances will never be garbage collected unless you explicitly release them. But perhaps that isn't an issue for you. Here's how I would tackle this:
function MyClass(id) {
this.id = id;
MyClass.instances[id] = this;
}
MyClass.instances = {};
MyClass.instanceFromId = function(id) {
return MyClass.instances[id];
}
Related
I'd like to show for example some stats in HTML subpage that are stored in Class object that is created while clicking on the button.
class Game {
constructor(start) {
this.stats = new Statistics();
this.wallet = new Wallet(start);
document.querySelector('.start').addEventListener('click', this.startGame.bind(this));
}
And for example I want to see stats from stats variable on another subpage.
Can you give me any suggestions?
Likely you want to store this in a global variable.
For example (not necessarily good practice):
Game.js
var currentGame = null;
class Game {
constructor() {
currentGame = this;
this.stats = new Statistics();
// ....
}
}
Now, you can just access your currentGame instance, which presumably there will be only one of. To get better knowledge on how to implement this pattern in a better way, research the Singleton pattern.
console.log(currentGame.stats)
Edit: the code below was made up on the spot to show how I was going about what I was doing. It definietely won't run, it is missing a lot of things.
Here is a working example in codepen: https://codepen.io/goducks/pen/XvgpYW
much shorter example: https://codepen.io/goducks/pen/ymXMyB
When creating a function that is using call or apply, the this value stays null when using getPerson. however, when I use apply or call with getPerson it returns the correct person.
Please critique, I am really starting to learn more and more. I am in the middle of a project section so it might be hard to change all the code, but my next project could implement this better.
call and apply are setting to the window and not the object.
I will provide code that is much simpler with the same concept of what I am talking about.
function createPerson(){
this.manager = null;
this.teamManager = null;
this.setTeamManager = function(val){
this.teamManager = val;
}
this.setManager = function(val){
console.log('setting manager to',val);
this.teamManager = val;
}
this.getTeamManager = function(){
console.log('setting team manager to',val);
return this.teamManager ;
}
this.getManager = function(){
return this.manager;
}
this.appendSelect = function(elem){
var that = this;
createOtherSelects(that,elem);
}
//some functions that create selects with managers etc
//now assume there are other selects that will filter down the teams,
//so we might have a function that creates on change events
function createOtherSelects(that){
//code that creates locations, depending on location chosen will
//filter the managers
$('#location').on('change',function(){
//do some stuff
//... then call create management
createManagement(that,elem);
});
}
function createManagement(that,elem){
var currentLocation = that.location; //works
var area = that.area;//works ... assume these are set above
//code that returns a filter and unique set of managers back
that.teamManager = [...new Set(
data.map(person=>{
if(person.area==area &&
person.currentLocation==currentLocation
){
return person;
}
})
)].filter(d=>{if(d){return d}});
if(elem.length>0){
var selectNames = ['selectManager','selectTeamManager'];
var fcns = [that.setManager,that.setTeamManager];
for(var i = 0; i < selectNames.length;i++){
//do stuff
if(certainCriteriaMet){
// filter items
if(filteredManager == 1){
fcns[i].call(null,currentManager);//
}
}
}
}
}
}
var xx = new createPerson()
In console I see setting manager and setting team manager to with the correct values.
however when I call xx in console, I see everything else set except for
xx.teamManager and xx.manager
instead it is applying to the window, so if I type teamManager in the console, it will return with the correct person.
If I straight up say
that.setManager('Steve')
or even it works just fine.
xx.setManager('steve')
the this value in setManager is somehow changing from the current instance of the object to this window. I don't know why, and I would like to learn how to use apply and call using that for future reference.
I think the issue is with your following code
fcns[i].call(null,currentManager)
If you are not supplying "this" to call, it will be replaced with global object in non-strict mode.
fcns[i].call(that,currentManager)
See mdn article here
From your codepen example, you need to change that line
fcnset[0].apply(that,[randomName]);
The first argument of the apply method is the context, if you are not giving it the context of your method it's using the global context be default. That's why you end up mutating the window object, and not the one you want !
First things first: I'm not sure whether the information that I'm going to provide will be enough, I will happily add additional information if needed.
I'm serializing a complex structure into the JSON-Format, Field[i][0] is the "this"-reference to an object.
Firebug's Output on JSON.Stringify(myObj)
This is all fine and working as long as I keep it all JS. But now I have the requirement to serialize and send it to my backend to get the reference + computed information back.
Now how do I map back to the reference I had before? How do I bind this ref back to an Object?
This $$hash thing looks internal and proprietarish so I havent even bothered trying something like Object[$$hash] = ref or whatever.
This general idea probably seems pretty whack, but the result is returned asynchrously and I need an identifier to bind the new information back to the original object. Obviously I could just make up my own identifier for that, but I was wondering whether there's an option to solve it this way.
EDIT
The objects are created like this (likewise)
var arrayOfObj = []
arrayOfObj.push(new Object.With.SomeSettersAndGetters());
The Object has a method like
function GetRef(){
return this;
}
Which I'm using to keep a ID/Ref through my code.
Thank you!
Update
If you want to update a series of instances and make many Ajax requests, then you need to look at Ajax long polling and queueing techniques. You won't be able to preserve the reference, but regardless of what Ajax technique you use, make use of the below trick to preserve the reference.
Add long polling on top and you're good to go.
The idea is this:
Assume the server will respond in JSON format. If you need to refer to the original references, here's my two cents:
Update the exact references when the server replies. Say you have 10 instances of Something stored in an array. On a successful response, you use the methods in the Something class to update the specific instances in whatever way you want.
/**
* The array with something instances.
* #type {Array.<Something>}
*/
var instances = [];
/**
* The Ajax success function.
* #param {Event} event The event object.
*/
function ajaxSuccess(event) {
var response = event.target.getResponseText();
var actualResponse = JSON.parse(response);
for (var i = 0, len = actualResponse.length; i++) {
instances[i].setWhatever(actualResponse[i].whatever);
};
};
The above is a more procedural approach. If you want full blown OOP in JS, then you think in modular design patterns. Say you have a module that loads data into some place. Basically, everything related to that module is an instance property.
var myModule = function() {
this.whatever = 1;
};
myModule.prototype.loadMore = function() {
var request = new XMLHttpRequest(),
that = this; // store a reference to this.
request.send(); // etc
request.onreadystatechange = that.onSucess;
};
myModule.prototype.onSucess = function(event) {
var response = JSON.parse(event.target.getResponseText());
this.whatever = response.whatever;
};
var moduleInstance = new myModule();
myModule.loadMore();
// Now the scope is always preserved. The callback function will be executed in the right scope.
Let's assume on the backend side of things, you have a model class that mimics your client side JavaScript model. Say you want to update a reference inside a model that displays text. I use Scala on the backend, but look at the fields/properties and ignore the syntax.
case class Article (
title: String,// these are my DB fields for an Article.
punchline: String,
content: String,
author: String
);
// now assume the client is making a request and the server returns the JSON
// for an article. So the reply would be something like:
{"title": "Sample title", "punchline": "whatever", "content": "bla bla bla boring", "author": "Charlie Sheen"};
// when you do
var response = JSON.parse(event.target.getResponseText());
// response will become a JavaScript object with the exact same properties.
// again, my backend choice is irrelevant.
// Now assume I am inside the success function, which gets called in the same scope
// as the original object, so it refers TO THE SAME THING.
// the trick is to maintain the reference with var that = this.
// otherwise the onSuccess function will be called in global scope.
// now because it's pointing to the same object.
// I can update whatever I want.
this.title = response.title;
this.punchline = response.punchline;
this.content = response.content;
this.author = response.author;
// or I can put it all in a single variable.
this.data = response;
What you need to remember is that scope needs to be preserved. That's the trick.
When I do var that = this; I copy a reference to the model instance. The reference is remembered through higher-order, not current scope.
Then I tell the XMLHttpRequest object to call that.ajaxSuccess when it is complete. Because I used that, the ajaxSuccess function will be called in the scope of the current object. So inside the ajaxSuccess function, this will point to the original this, the same instance.
JavaScript remembers it for me it when I write var that = this;
right now i am at a point where i feel that i need to improve my javascript skills because i already see that what i want to realize will get quite complex. I've iterrated over the same fragment of code now 4 times and i am still not sure if it's the best way.
The task:
A user of a webpage can add different forms to a webpage which i call modules. Each form provides different user inputs and needs to be handled differently. Forms/Modules of the same type can be added to the list of forms as the user likes.
My current solution:
To make the code more readable and seperate functions i use namespaced objects. The first object holds general tasks and refers to the individual forms via a map which holds several arrays where each contains the id of a form and the reference to the object which holds all the functions which need to be performed especially for that kind of form.
The structure looks more or less similar to this:
var module_handler = {
_map : [], /* Map {reference_to_obj, id} */
init: function(){
var module = example_module; /* Predefined for this example */
this.create(module);
},
create: function(module) {
//Store reference to obj id in map
this._map.push([module,id = this.createID()]);
module.create(id);
},
createID: function(id) {
//Recursive function to find an available id
},
remove: function(id) {
//Remove from map
var idx = this._map.indexOf(id);
if(idx!=-1) this._map.splice(idx, 1);
//Remove from DOM
$('#'+id+'').remove();
}
}
var example_module = {
create: function(id) {
//Insert html
$('#'+id+' > .module_edit_inner').replaceWith("<some html>");
}
}
Now comes my question ;-)
Is the idea with the map needed?
I mean: Isn't there something more elegant like:
var moduleXYZ = new example_module(id)
which copies the object and refers only to that form.... Something more logical and making speed improvements?? The main issue is that right now i need to traverse the DOM each time if i call for example "example_module.create() or later on any other function. With this structure i cant refer to the form like with something like "this"???
Do you see any improvements at this point??? This would help me very much!!! Really i am just scared to go the wrong way now looking at all the stuff i will put on top of this ;-)
Thank You!
I think you're looking for prototype:
=========
function exampleModule(id)
{
this.id = id;
}
exampleModule.prototype.create = function()
{
}
=========
var module1 = new exampleModule(123);
module1.create();
var module2 = new exampleModule(456);
module2.create();
I am working on a Javascript object that contains some YUI objects. The key thing is, my object needs to contain it's own set of YUI tabs so that I can display multiple instances of my object on the same page and have the tabs control their own object instance.
I set it up as follows:
var Scheduler = function(divid,startDate,mode){
this.tabView = null;
...
this.init = function(){
this.tabView.appendTo(this.calendar_cell);
this.tabView.addTab( new YAHOO.widget.Tab({
label: 'Day',
content:'<div id="'+ this.calendar_day_div +'" style="width:100%; height:auto;"></div>'
}));
var tab0 = this.tabView.getTab(0);
tab0.addListener('click', this.showWeek);
}
this.showWeek(){
alert(this);
}
});
Here's the problem. I would expect the alert(this); in this.showWeek to alert the instance of scheduler. Instead, it's giving me the tab li. I tried alerting this.parent and am given 'undefined' as an answer.
How should I set this up to do what I need to do?
The addListenter method takes a scope argument. So you can change your call to the following to solve your problem (since you are using YUI):
tab0.addListener('click', this.showWeek, undefined, this);
When you attach a function to an event of an object (in this case the object held by tab0) then its usually that object that becomes the this context of the function when it executes.
Adjust your code like this:-
var self = this;
this.showWeek(){
alert(self);
}