How to reference a variable dynamically in javascript - javascript

I am trying to reference a variable dynamically in javascript
The variable I am trying to call is amtgc1# (where # varies from 1-7)
I am using a while statement to loop through, and the value of the counting variable in my while statement corresponds with the last digit of the variable I am trying to call.
For Example:
var inc=3;
var step=0;
while(step < inc){
var dataString = dataString + amtgc1#;
var step = step+1;
}
Where # is based on the value of the variable "step". How do I go about doing this? Any help is appreciated! Thanks!!

Rather than defining amtgc1[1-7] as 7 different variables, instantiate them as an array instead. So your server code would emit:
var amtgc1 = [<what used to be amtgc11>,<what used to be amtgc12>, ...insert the rest here...];
Then, you can refer to them in your loop using array syntax:
var dataString = dataString + amtgc1[step];

The only way you can do this (afaik) is to throw all of your amtgc1# vars in an object such as:
myVars = {
amtgc1: 1234,
amtgc2: 12345,
amtgc3: 123456,
amtgc4: 1234567
};
Then you can reference it like
myVars["amtgc" + step];

How about:
var dataString = dataString + eval('amtgc1' + step);

It is true that eval() is not always recommended, but that would work. Otherwise depending on the scope, you can reference most things like an object in JavaScript. That said, here are examples of what you can do.
Global scope
var MyGlobalVar = 'secret message';
var dynamicVarName = 'MyGlobalVar';
console.log(window.[dynamicVarName]);
Function Scope
function x() {
this.df = 'secret';
console.log(this['df']);
}
x();

Not tested, but can't see why you can't do this...
$('#amtgc1' + step).whatever();

If your amtgc1* variables are defined as a property of an object, you can reference them by name. Assuming they are declared in the global scope, they will be members of the window object.
var inc=7;
var step=0;
while(step < inc){
var dataString = dataString + window['amtgc1'+(step+1)];
var step = step+1;
}
If they are defined in a different scope (within a function) but not belonging to any other object, you're stuck with eval, which is generally considered bad.
also, hooray for loops!
var inc=7;
for ( var step=0; step < inc; step++ ){
var dataString = dataString + window['amtgc1'+(step+1)];
}

I have built a way which you could solve this problem using objects to store the key values, where the key would be the reference to the task and the value will be the action (function) and you could use an if inside the loop to check the current task and trigger actions.
If you would like to compare dynamically concatenating strings with "variable", you should use the eval() function.
/* store all tasks references in a key value, where key will be
* the task reference and value will be action that the task will
* Execute
*/
var storeAllTasksRefer = {
amtgc11:function(){ alert("executing task amtgc11"); },
amtgc112:function(){ alert("executing task amtgc112"); },
"amtgc1123":"amtgc1123"
// add more tasks here...
};
var inc = 7;
var step = 1;
var dataString = 'amtgc1';
while(step <= inc){
var dataString = dataString + step;
//alert(dataString); // check its name;
step = step+1;
// check if it is my var
if( dataString == 'amtgc112' ){
// here I will reference my task
storeAllTasksRefer.amtgc112();
}// end if
/* you can also compare dynamically using the eval() function */
if('amtgc1123' == eval('storeAllTasksRefer.'+dataString)){
alert("This is my task: "+ eval('storeAllTasksRefer.'+dataString));
} // end this if
} // end while
Here is the live example: http://jsfiddle.net/danhdds/e757v8ph/
eval() function reference: http://www.w3schools.com/jsref/jsref_eval.asp

Related

How to dynamically change the contents of a function using JavaScript

To help understand this the function is in the html page and it is generated, I cannot change the generated code:
function Update_qu7260() {
var newVal = ''
for( var idx = 0; idx < 2; idx++ )
{
var test
if( idx == 0 ) test = text7263
else if( idx == 1 ) test = text7265
if( test.matchObj ) newVal += test.leftSel + "-" + test.matchObj.rightSel + ","
}
newVal = newVal.substring( 0, newVal.length-1 )
VarQuestion_0001.set( newVal )
qu7260.hasBeenProcessed=false;
doImmFeedback('qu7260');
}
var qu7260 = new Object();
...
qu7260.updFunc = Update_qu7260;
var qObj=[qu7260];
Note in the above the number "7260", the numbers start at 1 so there are lots of them and each Update_###() will be different so I cannot re-write them with "hard wired" code. My code is in an external JavaScript file and is executed onLoad:
...
var updFunc = qObj[0].updFunc.toString();
if(updFunc.indexOf('doImmFeedback(')!=-1){
updFunc = updFunc.replace('doImmFeedback','doImmQuestionFeedback'); // do my function
updFunc = updFunc.replace('function ',''); // remove the word function
var funcName = updFunc.substr(0,updFunc.indexOf('(')); // get the function name e.g. Update_qu7260
updFunc = "window['" + funcName + "']=function" + updFunc.replace(funcName,'');
eval(updFunc);
}
...
When I change the eval() to alert() I can see the that it's correct, however, the eval() is not raising any errors and my function doImmQuestionFeedback is not being called. When I subsequently do an alert(qObj[0].updFunc.toString()) I see the original function.
It would seem that I have provided information that is too complex, so the following code is a better example:
function hi(){alert('hi');}
function changeHi(){
hi(); // I get an alert box with hi
newHi = "function hi(){alert('hi there');}"
eval(newHi);
hi(); // I get an alert box with hi
window.setTimeout('hi()',500); // I get an alert box with hi
}
window.setTimeout('changeHi()',500);
The following is the original question:
I have a predefined function that I did not create, however, I know it's name so I can get the function itself and then I change it by doing:
var funcText = window.updateFunc.toString();
funcText = funcText.replace('doSomeOtherFunction(','doMyFunction(');
How do I update the actual function so it will do all that it did before except it will now call doMyFuntion()?
The following is an example to help visualize what I want to do, the actual function I need to change is very complex. I have:
function updateFunc(whatToUpdate,true){
... - do lots of stuff.
var retVal = doSomeOtherFunction(whatToUdate);
... - do lots of stuff based on retVal
}
I need to change this to:
function updateFunc(whatToUpdate,true){
... - do lots of stuff
var retVal = doMyFunction(whatToUdate);
... - do lots of stuff based on retVal, I have had a chance to change retVal
}
Then the first thing my function will do is call doSomeOtherFunction() check/change the returned value and subsequently return the value to the updateFunc().
I have tried to manipulate the funcText above to:
funcText = 'window.updateFunc = function(...';
eval(funcText);
Without success.
This may be closed enough to what you are looking for.
Assuming you have this original function:
function originalFunc(val) {
// this function converts input string to upper case
return val.toUpperCase();
}
Now you want to override it to something either before or after you execute that function (in this example, we execute before, of course before or after doesn't matter in this case).
// we preserve orignal function
var originalFunc_save = originalFunc;
// now we override the original function with this block
var originalFunc = function(text) {
// lets call the orignal function
text = originalFunc_save(text);
// now do our custom thing
return text.split('').reverse().join('');
}
So our test should work.
var text = 'This is a test';
console.log(originalFunc(text));
Output:
TSET A SI SIHT
This method also works if you have to override functions inside a class. The only thing we have to be careful of is to choose a saved name that doesn't interfere with the original class code. _save may not be good enough, but you get the idea.
UPDATE: I'm updating this code above to use a string variable pointing to the original function. I think this is what the OP wanted.
Original code which defined by some library
function originalFunc(val) {
// this function converts input string to upper case
return val.toUpperCase();
}
Now we use the func string variable to point to that function and execute it.
var text = 'This is a test';
var func = 'originalFunc';
text = window[func](text);
console.log(text);
Output: Of course we get the original intended result because we haven't overridden it.
THIS IS A TEST
Now we write our code to override the original function behavior using a string pointing to the function.
// let's define a new function string
var funcSaved = func + '___saved';
// now preserve the original function code
window[funcSaved] = window[func];
// override the original function code block
window[func] = function(text) {
// lets call the orignal function
text = window[funcSaved](text);
// now do our custom thing
return text.split('').reverse().join('');
}
// let's test the code
text = 'This is a test';
text = window[func](text);
console.log(text);
Output:
TSET A SI SIHT
You can make a clone of updateFunc function, edit it at your discretion and work with it in what follows.
function updateFunc(whatToUpdate, param){ // the initial function
...
var retVal = doSomeOtherFunction(whatToUpdate);
return retVal;
}
// formation of unnamed function as string
var newfunc = updateFunc.toString().replace('function updateFunc', 'function ').replace('doSomeOtherFunction(', 'doMyFunction(');
function doMyFunction(whatToUpdate){ // your new function, just for example
console.log(parseInt(whatToUpdate) * 10);
}
var newUpdateFunc;
// declaring new version of 'updateFunc' function
// which is stored in 'newUpdateFunc' variable
eval("newUpdateFunc = " + newfunc);
newUpdateFunc(3); // outputs '30'
I believe this is a valid use case for the forgotten JavaScript with feature.
Basic idea: you call original updateFunc supplying your own version of doSomeOtherFunction to it using with namespace injection:
function updateFunc(whatToUpdate,true){
... - do lots of stuff.
var retVal = doSomeOtherFunction(whatToUdate);
... - do lots of stuff based on retVal
}
function patchUpdateFunc() {
var original_doSomeOtherFunction = window.doSomeOtherFunction;
var original_updateFunc = window.updateFunc;
function doMyFunction() {
// call original_doSomeOtherFunction() here,
// do your own stuff here.
};
window.updateFunc = function() {
with ({doSomeOtherFunction: doMyFunction}) {
return original_updateFunc.apply(this, arguments);
}
}
}
patchUpdateFunc();
I think you are going at this way too complicated.
If you only have doMyFunction and doSomeOtherFunction to switch between, you could just create a flag somewhere telling you to use one or the other when used in an if-statement.
If you want to call a function with a name you do not know beforehand and you only get a name during runtime, you could either accept the function to call as a parameter or accept the name of the function as a parameter and call it like so: var retVal = window[functionName](); (assuming functionName is a property of the window object).
I would highly recommend directly accepting a function as a parameter since the function may not be defined in a global scope.
EDIT:
After your clarification, I think, I can give you a satisfying answer:
if you have a string like var functionString = "function updateFunc(whatToUpdate){var retVal = doMyFunction(whatToUpdate);}";
You can define a function using a Function object:
window.updateFunc = new Function("whatToUpdate", "return (" + functionString + ")(whatToUpdate)");
This will replace the already existing function and you can give it any valid function string you want as long as you know and specify the arguments.
If I understood correctly, you want to override the external function. You can achieve that with the following code
//Someone else's function
function externalFunction(foo){
return "some text";
}
//Your function
function myFunction(value){
//Do something
}
//Override
var externalFunction = (function(){
var original = externalFunction; //Save original function
return function(){
var externalFunctionReturnValue = original.apply(this, arguments);
return myFunction(externalFunctionReturnValue);
}
})();
I strongly sugest not to use eval, but since you want to parse javascript from string:
function hi(){alert('hi');}
function changedHi(){
hi(); // I get an alert box with hi
newHi = "window['hi'] = function(){alert('hi there');}"
eval(newHi);
hi(); // I get an alert box with hi there
window.setTimeout('hi()',500); // I get an alert box with hi there
}
window.setTimeout('changedHi()',500);
UPDATE:
This code snippet works which is your original code:
<script type="text/javascript">
function doImmFeedback(foo){
console.log("DoImmFeedback: " + foo);
}
function Update_qu7260() {
console.log("Some code")
doImmFeedback('qu7260');
}
</script>
<script type="text/javascript">
var qu7260 = new Object();
qu7260.updFunc = Update_qu7260;
var qObj=[qu7260];
var updFunc = qObj[0].updFunc.toString();
if(updFunc.indexOf('doImmFeedback(')!=-1){
updFunc = updFunc.replace('doImmFeedback','doImmQuestionFeedback'); // do my function
updFunc = updFunc.replace('function ',''); // remove the word function
var funcName = updFunc.substr(0,updFunc.indexOf('(')); // get the function name e.g. Update_qu7260
updFunc = "window['" + funcName + "']=function" + updFunc.replace(funcName,'');
console.log(updFunc);
eval(updFunc);
}
function doImmQuestionFeedback(foo){
//Your function
console.log("doImmQuestionFeedback: " + foo);
}
Update_qu7260(); //This executes your doImmQuestionFeedback
</script>
So if your function isn't running, your function isn't in the global scope, or something else is happening, and we can't know if don't have any more info. Check your developer's console for javascript errors.

why value is not changing of a `var` when it is in the scope of the function

just look at this question: What is the scope of variables in JavaScript?
in the answer which is accepted look at the point 3
according to him a will be 4 but now look at my function:
function JIO_compiler(bpo){ // bpo means that " behave property object " and from here i will now start saying behave property to behave object
var bobj = bpo, // bobj = behave object
bobj_keys = Object.keys(bobj), // bobj_keys = behave object keys. This willl return an array
Jcc_keys = Object.keys(JIO_compiler_components), // Jcc = JIO compiler components
function_code = ""; // get every thing written on every index
if (bobj.hasOwnProperty('target') === false) { // see if there is no target defined
console.log("No target is found"); // tell if there is no target property
throw("there is no target set on which JIO-ASC will act"); // throw an error
};
if (bobj.hasOwnProperty('target') === true) {
console.log("target has been set on "+ bobj['target']+" element"); //tell if the JIO-ASC has got target
function x(){
var target_ = document.getElementById(bobj['target']);
if (target_ === null) {throw('defined target should be ID of some element');};
function_code = "var target="+target_+";";
console.log("target has successfully been translated to javascript");//tell if done
};
};
for(var i = 0; i < bobj_keys.length; i++){
if(bobj_keys[i] === "$alert"){ // find if there is $alert on any index
var strToDisplay = bobj[bobj_keys[i]];
var _alert = JIO_compiler_components.$alert(strToDisplay);
function_code = function_code+ _alert;
};
};// end of main for loop
alert(function_code);
new Function(function_code)();
};
well it is big... but my problem is in the second if statement. now according to the accepted answer the value of function_code should change according to what is instructed. but when at last i alert the function code then it alert blank. i mean it should alert at least var target = something ; and the last console.log statement of this if statement is not showing text in the console.
so what is wrong in this ?
You're setting function_code inside the definition for function x(), but x() is never called. function_call won't change until you make a call to x.
thats because your variable is inside the function scope, you need to define your variable outside of it, as
function_code = "";
function JIO_compiler(bpo){
....
};// end of main for loop
//call the function
JIO_compiler(some_parameter);
//alert the variable
alert(function_code);
you need to call the function JIO_compiler() first so that the appropriate value is set to function_code variable from JIO_compiler() function, as

Creating functions dynamically in JS

I am creating the AI engine for a JS game, and it's made of Finite State Machines. I am loading the number of states and their variable values from the XML. I also want to load the behaviour, and since I don't have the time to create a scripting language, I thought it would be a good idea to 'insert' JS code on external files (inside XML nodes), and execute it on demand.
Something like that
<evilguy1>
<behaviour>
this.x++;
</behaviour>
<behaviour>
this.y++;
</behaviour>
</evilguy1>
To something like that:
function behaviour_1(){
this.x++;
}
function behaviour_2(){
this.y++;
}
My question is, now that I have the code loaded, how can I execute it? I would like to create a function with an unique name for each code 'node', and then call them from the game logic, but I don't know if this is possible (Since you can load more JS code from the HTML, you should also be able to do it from the JS code, no?). If not, is there any similar solution? Thanks in advance!
(PS:The less external-library-dependent, the better)
Edit 1:
Ok, so now I know how to create functions to contain the code
window[classname] = function() { ... };
Well, you could use Function constructor, like in this example:
var f = new Function('name', 'return alert("hello, " + name + "!");');
f('erick');
This way you're defining a new function with arguments and body and assigning it to a variable f. You could use a hashset and store many functions:
var fs = [];
fs['f1'] = new Function('name', 'return alert("hello, " + name + "!");');
fs['f1']('erick');
Loading xml depends if it is running on browser or server.
To extend Ericks answer about the Function constructor.
The Function constructor creates an anonymous function, which on runtime error would print out anonymous for each function (created using Function) in the call stack. Which could make debugging harder.
By using a utility function you can dynamically name your created functions and bypass that dilemma. This example also merges all the bodies of each function inside the functions array into one before returning everything as one named function.
const _createFn = function(name, functions, strict=false) {
var cr = `\n`, a = [ 'return function ' + name + '(p) {' ];
for(var i=0, j=functions.length; i<j; i++) {
var str = functions[i].toString();
var s = str.indexOf(cr) + 1;
a.push(str.substr(s, str.lastIndexOf(cr) - s));
}
if(strict == true) {
a.splice(1, 0, '\"use strict\";' + cr)
}
return new Function(a.join(cr) + cr + '}')();
}
A heads up about the Function constructor:
A function defined by a function expression inherits the current
scope. That is, the function forms a closure. On the other hand, a
function defined by a Function constructor does not inherit any scope
other than the global scope (which all functions inherit).
source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions#Differences
Assuming you have an array of node names and a parallel array of function body's:
var functions = {};
var behaviorsNames = ['behavior1', 'beahvior2'];
var behaviorsBodies = ['this.x++', 'this.y++'];
for (var i = 0; i < behaviorsNames.length; i++){
functions[behaviorsNames[i]] = new Function(behaviorsBodies[i]);
}
//run a function
functions.behavior1();
or as globals:
var behaviorsNames = ['behavior1', 'beahvior2'];
var behaviorsBodies = ['this.x++', 'this.y++'];
for (var i = 0; i < behaviors.length; i++){
window[behaviors[i]] = new Function(behaviorsBodies[i]);
}
All of the above answers use the new Function() approach which is not recommended as it effects your app performance. You should totally avoid this approach and use window[classname] = function() { ... }; as #user3018855 mention in his question.

use array outside the parent function

with this function triggered as a callback
var testing = [];
var vid;
function showMyVideos(data){
var feed = data.feed; //object "feed"
var entries = feed.entry || []; //array "entry"
var html = ['<ul>'];
for (var i = 0; i < entries.length; i++){
entry = entries[i];
playCount = entry.yt$statistics.viewCount.valueOf() + ' views';
title = entry.title.$t;
vid = (getVideoId(entry.link[0].href));
testing[i] = vid;
lnk = '<a href = \'' + entry.link[0].href + '\'>link</a>';
html.push('<li>', title, ', ', playCount, ', ', vid, ', ', lnk, '</li>');
}
html.push('</ul>');
$('#videoResultsDiv').html(html.join(''));
}
I want to use the "testing" array on other functions, how will I do that?.. I'm frustrated now, sorry I'm just starting to appreciate JavaScript. I want to perfectly access the array data like when I'm doing on
console.log(testing)
inside the function..
"testing" is in the global scope and it should be accesible anywhere as long as there isn't another variable in another scope with the same name overriding the global "testing".
If the values aren't accesible in the place you are trying it, it could be because that function is getting executed before the callback loads the values in "testing".
As has been mentioned testing is global scope, so you can use it anywhere. So it's important that you have the correct execution order for the functions using testing.
Some might frown on using global scope in javascript but there are ways to mitigate issues.
Generally when I have to implement global scope vars, I will create a method along the lines of 'clear_session' or 'reset_globals'.
In your case a function like that would look like this:
function reset_globals(){
testing = [];
vid;
}
This is useful because then you know that after you call reset_globals() that your variables are going to be empty.

remembering a javascript var each call to function

I've got a nice little shopping basket where if the user deletes an item, the slideup jquery function is called and is disappears.
In the back ground, I'm removing it from the db with a call to a code igniter controller.
function delete_booking(id_booking, amount) {
if (user_total == null || user_total == '') {
var user_total = parseFloat(0);
}
$.ajax({
type: "POST",
data: '',
url: "/"+id_booking,
});
$("#booking_id_"+id_booking).slideUp();
var user_total = (parseFloat() - parseFloat() - parseFloat(user_total));
alert(user_total);
alert(parseFloat(user_total));
$('#booking_total').html(user_total);
}
What I can't fathom, is when I update the user total: $('#booking_total')
I need somehow to have the function remember the NEW total...
I've googled 'remembering JavaScript var' and similar, but I can't find exactly what I need...
My only other option is to do another call to the db with ajax to get the new total, but there MUST be a JS way to do this?
If you are not navigating to a different page, you can store the value in a variable that is outside the function (whether just on the page in the global namespace or the within the module in which the function resides).
var total;
function delete_booking(...)
{
...
total = user_total;
}
as you have said
I need somehow to have the function
remember the NEW total
a function cant remember a value, while a variable can, to solve your problem do one the below
1
var finalTotal;
function delete_booking(...)
{
//your more codes
$('#booking_total').html(user_total);
finalTotal = user_total;
}
now variable finalTotal hold the value
2
whenever you feel that you need to access the total read this way
var total=parseint($('#booking_total').html(),10) || 0;
But think method 1 is better than method 2
Because I "hate" a global approach for this type of problem, here is a little closure which will do the job of "remembering" the value (although I'm not really sure what exactly should be saved...^^)
var delete_booking = (function() {
var booking_total = $("#booking_total"),
total_amount = parseFloat(booking_total.html()) || 1000; // what ever the value should be...
return function(id, m) {
$.ajax(/* ... */);
$("#booking_id_"+id).slideUp();
total_amount -= m;
booking_total.html(total_amount);
}
}());

Categories

Resources