I'm not into JavaScript OOP, so I've made an object with some fields which contains some functions to invoke.
var test = {
questions: [],
addQuestion: function(questionTitle, possibleAnwsers)
{
// not really important
},
appendQuestionToHTML: function(question)
{
// not really important
},
makeQuestionFieldsEditable: function($questionNode)
{
$questionNode.find(".questionTitle").first(function(){this.changeTextOnClick($(this));});
$questionNode.find(".questionChoice").each(function(){this.changeTextOnClick($(this));});
},
changeTextOnClick: function($spanElement)
{
// not really important
}
};
Following object in makeQuestionFieldsEditable() function looks for ".questionTitle"-class node and all of ".questionChoice"-class nodes invoke another function for them.
The problem is that using this in anonymous function references to itself, not function saved on field changeTextOnClick.
Javascript/JQuery wants to invoke this function on HTMLDivElement, which doesn't exists.
Is there any solution?
You can do the trick using a reference to your this variable :
makeQuestionFieldsEditable: function($questionNode)
{
var that = this;
$questionNode.find(".questionTitle").first(function(){that.changeTextOnClick($(this));});
$questionNode.find(".questionChoice").each(function(){that.changeTextOnClick($(this));});
},
I think all you need to do is change 'this' to 'test' (the variable you have assigned this object to).
Related
I have a simple requirement, I need add the same code to hundreds of other JavaScript functions, the code can be executed at the end of the function, is there a handy way of doing it, like attach an function to another function dynamically, I think yes, because JavaScript is so powerful and too powerful, any ideas?
Note, I need dynamically assign new code or function to existing functions without change existing function's code, please give a solid solution, I can do it in hacky way, but no hacky way please!
The first method that comes to mind is simply create another function:
function primaryFunction() {
// ...
utilityMethod();
}
function otherPrimaryFunction() {
// ...
utilityMethod();
}
function utilityMethod() { ... }
Now utilityMethod() gets called from the end of each other primary function.
There's also a method which requires more code refactoring but is better in the long term: classes/prototypes.
Essentially, you have one "constructor" function which takes a number of parameters for the "class" and returns an class-like object:
function constructor(someClassField, anotherField) {
this.aField = someClassField;
this.fieldTwo = anotherField;
return this;
}
Now if you call this and pass some parameters, you get a class out:
var myClass = new constructor("1", "2");
myClass.aField == "1";
myClass.fieldTwo == "2";
So: If you define your utility method as above, then you can use this: for every primary function you instantiate a new instance of the constructor, with the final code looking like this:
function constructor(primaryFunction) {
this.function = primaryFunction;
this.call = function() {
this.function();
utilityMethod();
}
this.call();
return this;
}
function utilityMethod() { ... }
var primaryMethod = new constructor(function() { ... });
The creation of primaryMethod now automatically calls the primary function followed by the utility method, before returning the object so you can re-call both if you want to.
I am trying to build a lib and I need to call functions dynamically depending on the variables I have in parameter like this
strategies = min
function dispatchRuleToStrategy(strategies)
{
$.each(strategies, function(index, value) {
strategy = "strategy_" + value;
});
}
function strategy_min()
{
// do something
}
How can I call the function strategy_min() from dispatchRuleToStrategy()?
I've been trying a couple of things none of which are working.
Thanks for your help
Use an Object to create a dictionary of your functions e.g. lib
var lib = {
'strategy_min': strategy_min
};
then you can invoke via the key in this dictionary Object
lib['strategy_min']();
If you've named all your functions and you don't want to re-type the names over and over, you could
var lib = {};
function addToLib(fn) {
lib[fn.name] = fn;
}
// then
addToLib(strategy_min);
// or
[strategy_min].forEach(addToLib);
Put them in an object and use the property name:
var strategy_table = {
min: function() {
// do something
},
max: function() {
// do something else
},
...
};
Then you can access them as strategy_table[value]:
$.each(strategies, function(index, value) {
strategy_table[value]();
});
Others have already suggested to create a wrapper object for the functions, however, if your strategy_min() function is in the global scope, you can access it directly:
window['strategy_' + value]();
window in browsers refers to the global object. The bracket notation is used to access properties whose keys are dynamically generated. This way you are accessing the function, which is a property of the global object, i.e. window, and calling it using the parentheses.
Finally I found the real problem. I was in a jquery document ready which is a closure. I did not knew what closures were before today.
Thanks all for your help
You can use eval() function in the following manner
$.each(strategies, function(index, value) {
strategy = "strategy_" + value;
eval(strategy+"()");
});
I have the 'phone_dlg_manager' constructor function and its private methods show and init_country_code_combobox. The dialog reference is held in the phone_dlg variable. The show method triggers init_country_code_combobox and I have two options:
1) Explicitly pass the variable country_combobox that the init_country_code_combobox methods needs:
function phone_dlg_manager(ctx, open_dlg_button, edit_ctrl, item)
{
var phone_dlg;
show();
function show()
{
phone_dlg = ctx.application.ui.create_dialog(0, "PhoneEditorDlg");
init_country_code_combobox(phone_dlg.country);
read_to_dialog_controls(this._form_item);
phone_dlg.visible = true;
}
function init_country_code_combobox(country_combobox)
{
country_combobox.items.clear();
country_combobox.items.start_adding();
country_combobox.items.finish_adding();
}
}
2) Since phone_dlg is accessible withing init_country_code_combobox through closure, I can access the property that I need without explicitly passing the variable:
function phone_dlg_manager(ctx, open_dlg_button, edit_ctrl, item)
{
var phone_dlg;
show();
function show()
{
phone_dlg = ctx.application.ui.create_dialog(0, "PhoneEditorDlg");
init_country_code_combobox(phone_dlg.country);
read_to_dialog_controls(this._form_item);
phone_dlg.visible = true;
}
function init_country_code_combobox()
{
var country_combobox = phone_dlg.country;
country_combobox.items.clear();
country_combobox.items.start_adding();
country_combobox.items.finish_adding();
}
}
The second option seems easier to understand when reading code, however it makes the init_country_code_combobox function know more than it needs. Which option should I choose?
Thanks
This is mostly a matter of style. Option 1 is a little cleaner, and more extensible, since you can use init_country_code_combobox() to initialize more than just the one dialog. But if this is unlikely to be necessary, option 2 is not unreasonable.
This might seem like a noob question but I'm not sure what to do. I have function with 2 variables.
function someInfo(myVar1,myVar2)
{
this.lmyVar1=myVar1;
this.myVar2=myVar2;
}
myInstance=new someInfo("string1","string2");
function drawVariables(){
document.write(myInstance.myVar1);
document.write(myInstance.myVar2);
}
I want to use the same drawVariable() for multiple instances. I just can't figure out how the exact syntax for that. How can I make drawVariable() use a different instance of someInfo without repeating anything? Is there a simple example or tutorial I can follow?
Add an argument to the definition of function drawVariables. In the code below, this argument is called info. Now you can use info as your object inside the drawVariables function, and while calling drawVariables function, you can pass whatever instance you want to pass it. drawVariables function would now work with whatever instance you pass it while calling.
function someInfo(myVar1,myVar2)
{
this.myVar1=myVar1;
this.myVar2=myVar2;
}
// Create two separate instances
myInstance=new someInfo("string1", "string1");
myInstance2 = new someInfo("string2", "string2");
// info is the argument that represents the instance passed to this function
function drawVariables(info){
alert(info.myVar1 + ", " + info.myVar2);
}
// Call the function twice with different instances
drawVariables(myInstance);
drawVariables(myInstance2);
See http://jsfiddle.net/WLHuL/ for a demo.
function drawVariables(instance){
document.write(instance.myVar1);
document.write(instance.myVar2);
}
Would it make sense for you to do it this way?
function someInfo(myVar1, myVar2)
{
this.lmyVar1 = myVar1;
this.myVar2 = myVar2;
this.drawVariables = function ()
{
document.write(this.lmyVar1);
document.write(this.myVar2);
}
}
function Test()
{
var obj1 = new someInfo("aaa", "bbb");
var obj2 = new someInfo("xxx", "zzz");
obj1.drawVariables();
obj2.drawVariables();
}
Can anyone tell me why my 'showDiv_boo' is undefined inside the class´s method?
I also can´t access my class´s methods.
Here´s my class 'Blink' class with its properties and methods:
function Blink(div) {
this.div = div
}
Blink.prototype.counter = 0
Blink.prototype.showDiv_boo = true
Blink.prototype.showDiv = function() {
this.div.style.visibility = 'visible'
}
Blink.prototype.hideDiv = function() {
this.div.style.visibility = 'hidden'
}
Blink.prototype.startEngine = function() {
if (this.showDiv_boo) {
this.showDiv()
} else if (!this.showDiv_boo) {
this.hideDiv()
}
this.showDiv_boo = !this.showDiv_boo
this.counter++
}
Blink.prototype.startEffect = function() {
this.idEffect = setInterval(this.startEngine, 1000 / 45)
}
So, if I create:
_blink = new Blink(myDiv);
_blink.startEffect();
You can test... the variable 'showDiv_boo', is undefined inside the method.
Even, if I set the showDiv_boo inside the method to true, it won´t call my class´s methods showDiv or hideDiv.
Anyone?
Thanks :)
The reason why is that startEngine is called from setInterval. The way in which this callback is invoked causes startEngine to have a different value for this than startEffect. You need to save this in order to maintain it in the callback. For example.
Blink.prototype.startEffect = function () {
var self = this;
self.idEffect = setInterval(function () { self.startEngine(); }, 1000 / 45);
};
You need to:
use var self and call the method via self.startEngine()
use an anonymous function to wrap the call in [1] i.e. function(){ self.startEngine(); }
This is because when you just pass this.startEngine or self.startEngine you are just passing the function startEngine without specifying what this is, which in both cases is supplied by the global conext of DOMWindow.
To give an example...
function startEngine() {
...code omitted...
};
Blink.prototype.startEngine = startEngine;
Blink.prototype.start = function() {
setTimeout(startEngine, 0); // obviously wrong, what is this?
setTimeout(Blink.startEngine, 0); // actually the same as line above, although not as obvious
setTimeout(startEngine.bind(this), 0); // works correctly
}
works to add code to the prototype and if used in the anonymous function will work as expected, but if you just use Blink.startEngine as the callback it is exactly the same as using startEngine only the second is more obviously wrong because there's no object it is being called on so you'd expect this to be whatever is supplied by the context.
The other way you could do this without using the anonymous function would be
Blink.startEngine.bind(self)
Which returns a function that will call startEngine with the correct this same as explicitly creating the anonymous function and wrapping the call to self.startEngine()
Heres a link to a fiddle to play around with the differences: http://jsfiddle.net/bonza_labs/MdeTF/
If you do the following, you will find it is defined
var x = new Blink('hello');
x.showDiv_boo
Javascript uses prototypical inheritance. While showDiv_boo may not be explicitly defined within the instance of Blink that you now have, it does exist within the prototype that Blink inherits from. When you try referencing showDiv_boo from within the object, the Javascript engine realizes the object does not own a member by that name and then will check its prototype.
Along with setting a temporal variable to store this, you must call the startEngine() function with that variable:
Blink.prototype.startEffect = function(){
var self = this;
self.idEffect = setInterval(function(){ self.startEngine.call(self); }, 1000/45);
}
Note the .call(self), which basically calls the function with the variable self, so the variable this in startEngine will be the correct one.