284

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
Brett DeWoody
  • 59,771
  • 29
  • 135
  • 184
SpoiledTechie.com
  • 10,515
  • 23
  • 77
  • 100

13 Answers13

384

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);
}
Matthew Hegarty
  • 3,791
  • 2
  • 27
  • 42
PatrikAkerstrand
  • 45,315
  • 11
  • 79
  • 94
  • I gave the answer to Machine because of the article. Everyone else was saying that Eval was the best method and then Machine chimed in with the article which def puts the Eval item into perspective and makes me decide not to use it. – SpoiledTechie.com May 26 '09 at 20:49
  • 4
    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. – James Moore Jun 02 '10 at 13:56
  • how do i write the function name? just the function name? or with the "()"? – Netorica May 24 '12 at 07:23
  • @Mahan See my edit for an explanation. Is that what you were asking? – PatrikAkerstrand May 24 '12 at 07:31
  • 32
    but what if the function is not in global(window) scope? – Netorica May 31 '12 at 09:39
  • So use the scope where the function is. window is a object, if the function is in different object you can do myobj["functionName"]() – Krym Sep 04 '12 at 14:29
  • 1
    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. – NGauthier Jan 29 '14 at 13:53
  • For a simpler explanation, this is how it looks: Object[string](params). – LasagnaAndroid Dec 11 '14 at 21:48
  • Hi, i know this is pretty old now but example above, var fn = windows.settings.functionName. Doesn't this result in typeof === 'string' ? How is this ever a 'function'? – uptownhr May 01 '15 at 17:58
  • 1
    @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. – Pluc May 08 '15 at 15:24
  • i don't think this works for closures though. EG `(function(){ function test(){console.log("test")}; function callTest(){ var name = "test"; var fn = window[name]; fn(); } callTest(); })()` will throw and error so this is not suitable for use with jQuery event's E.G `$(function(){ });` (just a note to everyone) – Barkermn01 Sep 27 '15 at 20:35
  • And a note to not using eval i ban you from using jQuery or Prototype :D https://javascriptweblog.wordpress.com/2010/04/19/how-evil-is-eval/ – Barkermn01 Sep 27 '15 at 20:49
  • depending on how the namespace was done, it might be necessary to use the style window[settings][functionName] instead of window[settings.functionname]; – Stefan Steiger Jun 28 '18 at 13:16
  • 2
    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? – zipper May 16 '19 at 04:07
81
window[settings.functionName](t.parentNode.id);

No need for an eval()

Fabien Ménager
  • 140,109
  • 3
  • 41
  • 60
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.

Siamak Motlagh
  • 5,028
  • 7
  • 41
  • 65
NGauthier
  • 885
  • 8
  • 17
  • 1
    nice, I was using multiple scoped WinJS.Namespace.define() and I was able to dynamically call random functions from it thanks to you. – Cœur Mar 18 '13 at 15:59
  • +1 for making it work in any scope. I'm using TypeScript, and set "scope=this" to access functions in my class. Works perfect! – Vern Jensen Sep 11 '15 at 00:51
  • Worked for me. Thanks NGauthier – Ananda Prasad Bandaru Jan 28 '16 at 09:05
  • 1
    Spent almost half a day searching google for an answer. This is a fantastic solution! +1 – Krik Dec 29 '16 at 18:25
  • I don't have experience with this syntax but I'm driving crazy for this. Can you explain how to use it? – rivaldid May 05 '22 at 11:56
  • 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. – NGauthier May 05 '22 at 18:36
17

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

eval(settings.functionName + '(' + t.parentNode.id + ')');
Andrew Hare
  • 344,730
  • 71
  • 640
  • 635
  • Wierd - is there a reason this was downvoted? – Andrew Hare May 26 '09 at 20:43
  • 14
    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. – PatrikAkerstrand May 26 '09 at 20:50
  • 1
    Look at Machines answer. – SpoiledTechie.com May 26 '09 at 20:50
  • 5
    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. – James Moore Jun 02 '10 at 13:59
  • for devs stuck in IE, debugging evals tends to be a horrible experience, please avoid them. – Alex Nolasco Aug 03 '12 at 21:46
  • Eval is insecure and evil and it is the source of all the inhumanity in the universe, it is the reason behind orphans – Shereef Marzouk Sep 23 '12 at 07:15
  • 22
    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. – Michael J. Calkins May 11 '13 at 15:57
  • 3
    Eval is not "evil", but it can be slow and is certainly unsecure. – NGauthier Apr 16 '14 at 14:19
  • 1
    Whats the point, anything client side web based is insecure already and there are many ways to inject some malicious JavaScript into a web page. Never trust the client and do proper server side validation. – Gerrit Brink Sep 25 '14 at 17:24
  • Eval is Evil came from ASP.WebForms. WebForms were just Evil on its own and using .Net Eval made it even worse. Never the less, there always seems to be another way to do something in JS without using Eval() to micro manage speed, or just make the script more concise. But to this day Classic WebForms still use Evil Evals. – Piotr Kula Apr 14 '15 at 12:49
  • 1
    this is the correct answer, all the other answers are answering a different question: how can I do this without using eval? – kztd Jul 24 '21 at 14:35
  • Upvoted this because, until `eval` is abusively removed from Javascript to satisfy some people being hysterical about unlikely 'attackers' and not being able to 'debug' things, it gets the job done and using it is the programmer's choice alone. Turning programming into an evil vs good practices (starting with semantics and syntax and ending with what functions you're allowed to use) is a very unproductive idea. Functionality and performance should be the only concerns, and the only valid argument against `eval` in that regard is that the function is slow compared to the alternatives. – Yin Cognyto Sep 25 '22 at 19:17
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.)

Jesse Millikan
  • 3,104
  • 1
  • 21
  • 32
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.

Paco
  • 4,520
  • 3
  • 29
  • 53
nsimeonov
  • 704
  • 8
  • 18
  • The function constructor is a form of Eval https://jslinterrors.com/the-function-constructor-is-eval – FDisk Mar 17 '16 at 08:16
  • 1
    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." – nsimeonov Mar 17 '16 at 11:17
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.

Community
  • 1
  • 1
B Rad C
  • 510
  • 2
  • 6
  • 18
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);
}
Gumbo
  • 643,351
  • 109
  • 780
  • 844
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!

  • See the comment of Fabien Ménager just below [his answer](http://stackoverflow.com/a/912631/938111) -> *"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 ;) – oHo Nov 08 '13 at 09:39
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.

nackjicholson
  • 4,557
  • 4
  • 37
  • 35
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);
2

I prefer to use something like this:

window.callbackClass['newFunctionName'] = function(data) { console.log(data) };
...
window.callbackClass['newFunctionName'](data);
Tom Johnson
  • 121
  • 1
  • 6
  • 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. – mjhm Mar 06 '12 at 15:57
  • 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. – mbokil Aug 25 '13 at 15:59
-7
eval("javascript code");

it is extensively used when dealing with JSON.

  • 6
  • Eval is insecure and evil and it is the source of all the inhumanity in the universe, it is the reason behind orphans – Shereef Marzouk Sep 23 '12 at 07:15
  • 1
    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... – NGauthier Oct 30 '12 at 12:55
  • 4
    "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. – Gerrit Brink Sep 30 '13 at 09:41
  • @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. – Shawn Whinnery Mar 20 '15 at 18:01
  • @Grep Many people share require.js modules accross their client side and a NodeJS server. Eval is absolutely insecure on a server so any JS dev should make a habbit to do without it. On the client side, it's still certain that it will be slower especially if called several times, since you could interfere with JIT severely. – NGauthier May 29 '15 at 11:57