287

I got a string like:

settings.functionName + '(' + t.parentNode.id + ')';

that I want to translate into a function call like so:

clickedOnItem(IdofParent);

This of course will have to be done in JavaScript. When I do an alert on settings.functionName + '(' + t.parentNode.id + ')'; it seems to get everything correct. I just need to call the function that it would translate into.

Legend:

settings.functionName = clickedOnItem

t.parentNode.id = IdofParent
1
  • What type is settings.functionName? Commented May 26, 2009 at 20:37

13 Answers 13

386

Seeing as I hate eval, and I am not alone:

var fn = window[settings.functionName];
if(typeof fn === 'function') {
    fn(t.parentNode.id);
}

Edit: In reply to @Mahan's comment: In this particular case, settings.functionName would be "clickedOnItem". This would, at runtime translate var fn = window[settings.functionName]; into var fn = window["clickedOnItem"], which would obtain a reference to function clickedOnItem (nodeId) {}. Once we have a reference to a function inside a variable, we can call this function by "calling the variable", i.e. fn(t.parentNode.id), which equals clickedOnItem(t.parentNode.id), which was what the OP wanted.

More full example:

/* Somewhere: */
window.settings = {
  /* [..] Other settings */
  functionName: 'clickedOnItem'
  /* , [..] More settings */
};

/* Later */
function clickedOnItem (nodeId) {
  /* Some cool event handling code here */
}

/* Even later */
var fn = window[settings.functionName]; 
/* note that settings.functionName could also be written
   as window.settings.functionName. In this case, we use the fact that window
   is the implied scope of global variables. */
if(typeof fn === 'function') {
    fn(t.parentNode.id);
}
Sign up to request clarification or add additional context in comments.

14 Comments

Given the original question, I think the "if(typeof..." code isn't correct. Just call window[settings.functionName](t.parentNode.id). You'll get a TypeError if the function doesn't exist, and that's better than silently swallowing the problem and calling nothing.
but what if the function is not in global(window) scope?
See my awnser for a generic method retrieving function from string, even if inside several closures. Ideal if you want to register callbacks inside DOM attributes.
@uptownhr window.settings.functionName does. window[window.settings.functionName] does now. In javascript, object members can be access with a string the same way you do with an array. Assuming your function is declared in the global scope, it become a function in the window object. You can call it using a string.
It seems if with no "I hate eval" one will be politically wrong. What is the best approach for node.js and the expression string is from database?
|
81
window[settings.functionName](t.parentNode.id);

No need for an eval()

5 Comments

Nice. This is assuming, of course, the function was not declared in a different scope, but even then I would imagine you could do this within that scope...
It works if the desired function is in the global scope, if not, replace window by the function's scope.
Worked well. Even used less code than the eval function call.
Nice to see another fella avoiding eval when possible.
Does not work in closure code though. (function(){})();
72

Here is a more generic way to do the same, while supporting scopes :

// Get function from string, with or without scopes (by Nicolas Gauthier)
window.getFunctionFromString = function(string)
{
    var scope = window;
    var scopeSplit = string.split('.');
    for (i = 0; i < scopeSplit.length - 1; i++)
    {
        scope = scope[scopeSplit[i]];

        if (scope == undefined) return;
    }

    return scope[scopeSplit[scopeSplit.length - 1]];
}

Hope it can help some people out.

5 Comments

nice, I was using multiple scoped WinJS.Namespace.define() and I was able to dynamically call random functions from it thanks to you.
+1 for making it work in any scope. I'm using TypeScript, and set "scope=this" to access functions in my class. Works perfect!
Spent almost half a day searching google for an answer. This is a fantastic solution! +1
I don't have experience with this syntax but I'm driving crazy for this. Can you explain how to use it?
it's been 10 years, but if I recall correcly, given a string like "location.search" it would give you back the function object for window.location.search, which you can then call. This is safer and should be faster than doing a straight eval.
17

JavaScript has an eval function that evaluates a string and executes it as code:

eval(settings.functionName + '(' + t.parentNode.id + ')');

12 Comments

I could downvote it for using eval in an appropriate situation. No need of eval here. Better to find the function as a property of an object. Eval is slow and a bit like using a sledgehammer to swat a fly.
Look at Machines answer.
eval is a common idiom. If you don't like it, you're probably going to hate Javascript in general. IMHO, eval is the correct answer here. Machine's answer is OK if you remove the if-typedef check, but it adds code for no reason. If there's something wrong with the string you're passed, you've got a problem in either scenario.
You guys are a bunch of followers following a quote that just rhymes. If you ask any of these "eval is evil" people why it's actually evil without them scurrying off to google, you would get no answer. This is a classic example of sheep programming. If you don't understand it, remain silent and research.
Eval is not "evil", but it can be slow and is certainly unsecure.
|
15

eval() is the function you need to do that, but I'd advise trying one of these things to minimize the use of eval. Hopefully one of them will make sense to you.

Store the function

Store the function as a function, not as a string, and use it as a function later. Where you actually store the function is up to you.

var funcForLater = clickedOnItem;

// later is now
funcForLater(t.parentNode.id);

or

someObject.funcForLater = clickedOnItem;    
// later is now    
(someObject.funcForLater)(t.parentNode.id);

Store function name

Even if you have to store the function name as a string, you can minimize complexity by doing

(eval(settings.functionName))(t.parentNode.id);

which minimizes the amount of Javascript you have to construct and eval.

Dictionary of handlers

Put all of the action functions you might need into an object, and call them dictionary-style using the string.

// global
itemActions = { click: clickedOnItem, rightClick: rightClickedOnItem /* etc */ };

// Later...
var actionName = "click"; // Or wherever you got the action name
var actionToDo = itemActions[actionName];
actionToDo(t.parentNode.id);

(Minor note: If instead here you used syntax itemActions[actionName](t.parentNode.id); then the function would be called as a method of itemActions.)

1 Comment

I really liked the dictionary handler approach - worked very well for me - thanks.
12

While I like the first answer and I hate eval, I'd like to add that there's another way (similar to eval) so if you can go around it and not use it, you better do. But in some cases you may want to call some javascript code before or after some ajax call and if you have this code in a custom attribute instead of ajax you could use this:

    var executeBefore = $(el).attr("data-execute-before-ajax");
    if (executeBefore != "") {
        var fn = new Function(executeBefore);
        fn();
    }

Or eventually store this in a function cache if you may need to call it multiple times.

Again - don't use eval or this method if you have another way to do that.

2 Comments

The function constructor is a form of Eval jslinterrors.com/the-function-constructor-is-eval
Yes, they are similar, but not identical. "new Function() parses the JavaScript code stored in a string into a function object, which can then be called. It cannot access local variables because the code runs in a separate scope."
10

I wanted to be able to take a function name as a string, call it, AND pass an argument to the function. I couldn't get the selected answer for this question to do that, but this answer explained it exactly, and here is a short demo.

function test_function(argument)    {
    alert('This function ' + argument); 
}

functionName = 'test_function';

window[functionName]('works!');

This also works with multiple arguments.

Comments

8

If settings.functionName is already a function, you could do this:

settings.functionName(t.parentNode.id);

Otherwise this should also work if settings.functionName is just the name of the function:

if (typeof window[settings.functionName] == "function") {
    window[settings.functionName](t.parentNode.id);
}

Comments

3

This took me a while to figure out, as the conventional window['someFunctionName']() did not work for me at first. The names of my functions were being pulled as an AJAX response from a database. Also, for whatever reason, my functions were declared outside of the scope of the window, so in order to fix this I had to rewrite the functions I was calling from

function someFunctionName() {}

to

window.someFunctionName = function() {}

and from there I could call window['someFunctionName']() with ease. I hope this helps someone!

1 Comment

See the comment of Fabien Ménager just below his answer -> "if your function is not in the global scope, replace window by the function's scope." Please update your answer (you can provide that issue as a complement) Cheers ;)
2

I prefer to use something like this:

window.callbackClass['newFunctionName'] = function(data) { console.log(data) };
...
window.callbackClass['newFunctionName'](data);

2 Comments

Welcome to StackOverflow. It would be great to have a little more explanation about why. It made hit the books to see if "callbackClass" is a standard member of "window", but now I'm guessing that you're just trying keep from polluting the window namespace.
callbackClass in the example is a new property name being added to the window object it is not built-in. So Tom is setting a window variable that points to a function.
2

Based on Nicolas Gauthier answer:

var strng = 'someobj.someCallback';
var data = 'someData';

var func = window;
var funcSplit = strng.split('.');
for(i = 0;i < funcSplit.length;i++){
   //We maybe can check typeof and break the bucle if typeof != function
   func = func[funcSplit[i]];
}
func(data);

Comments

2

In javascript that uses the CommonJS spec, like node.js for instance you can do what I show below. Which is pretty handy for accessing a variable by a string even if its not defined on the window object. If there is a class named MyClass, defined within a CommonJS module named MyClass.js

// MyClass.js
var MyClass = function() {
    // I do stuff in here. Probably return an object
    return {
       foo: "bar"
    }
}

module.exports = MyClass;

You can then do this nice bit o witchcraft from another file called MyOtherFile.js

// MyOtherFile.js

var myString = "MyClass";

var MyClass = require('./' + myString);
var obj = new MyClass();

console.log(obj.foo); // returns "bar"

One more reason why CommonJS is such a pleasure.

Comments

-7
eval("javascript code");

it is extensively used when dealing with JSON.

6 Comments

Eval is evil....
Eval is insecure and evil and it is the source of all the inhumanity in the universe, it is the reason behind orphans
Eval is both slow and unsecure. See my awnser for a eval-less, generic solution that even supports scopes (ie: "module.submodule.function"). Works like a charm in apps heavily using JSON and AJAX...
"insecure and evil and it is the source of all the inhumanity in the universe" I do see this quite often, but for the 'insecure' part really gets to me when javascript is distributed to your browser (always viewable) and when we can modify any HTML or JAVASCRIPT on any page anyways. I don't use eval, but saying its insecure is pointless due to it being well JAVASCRIPT. Only js which is safe is server side js and using it there could be an issue, but on the front end anything can be modified anyways.
@Grep THANK YOU! "eval is evil" is an archaic lie. ALL of your client side javascript is delivered to the client in the form of a string which is then evaluated. The only difference between a vanilla page load and an eval("1+1") is WHEN it is evaluated. If my server were so compromised that it were distributing questionable javascript, I find it hard to believe that the intruder would ever touch the eval function as it would be FLIPPING POINTLESS.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.