61

In JavaScript, if I have a string in a variable, is there a way to get a reference to the function object which has that matching name? Note that jQuery is available to me so I can use any of its helper methods also.

For example:

myFunction = function(){};

var func_name = "myFunction";
var myFunctionPtr = ???  //how to get the function from the name

Thanks

solsberg
  • 7,875
  • 4
  • 22
  • 12

11 Answers11

79

if you know that its a global function you can use:

var functPtr = window[func_name];
//functPtr()

Otherwise replace window with the parent object containing the function.

mkoryak
  • 57,086
  • 61
  • 201
  • 257
26

I just did a quick test in Firebug and I was able to get the function from the name by simply eval()ing the name... I feel dirty using eval(), but it seems to get the job done here quite nicely.

var myFunctionPtr = eval(func_name);
rmeador
  • 25,504
  • 18
  • 62
  • 103
21

This is never a preferred approach. Instead of keeping the function name in func_name, you could have kept the reference to a function just as successfully in something like func_to_call.

If you absolutely require to keep the function reference as a string, you would typically use a hash table to map an arbitrary name to a variable (JS has first-class functions which makes it possible)

myFunction = function(){};   
var obj = {func_name: myFunction};

obj['func_name']();//executes the function

Fiddled (I don't know why, it's a such a tiny script :)

It was suggested that you use eval(func_name) - however this could quickly get out of control because of JS scoping.

You have also declared your myFunction = function(){}; as a global variable. On one hand, it lets you reference it as window[func_name] but on the other hand it pollutes the global scope.

Oleg
  • 24,465
  • 8
  • 61
  • 91
  • 2
    +1 This solution works and surprisingly was never upvoted. I found it the most elegant one for my use case. – bernardn Dec 04 '13 at 12:59
  • even better for simple stuff you could just pass a delegate around – JonnyRaa Jun 20 '14 at 13:54
  • Instantiating a control from MVC Razor, so the callback function name must come in as a C# string. Therefore, eval is the simplest choice. – Triynko Jul 13 '15 at 17:26
12

It depends on where and how the function is (or isn't) declared.

If it's a global and not declared via let name = ... or const name = ... syntax (and it's not a class constructor declared with class), you can check by looking for it as a property on the global object. (Those caveats are all ES2015 things; more below.) You can get a reference to the global object via this in loose mode at global scope; browsers also give you a global called window. So assuming a browser:

if (typeof window[func_name] === "function") {
    // ....
}

If it might not be a global, but rather is just in scope because your code closes over it, or if it was created using one of those ES2015 mechanisms I mentioned, there's really no good way to check other than eval:

if (eval("typeof " + func_name) === "function") {
    // ....
}

Using eval is a last resort, and you must only use it with strictly-controlled input. But when you have to, and you have strictly-controlled input, it's fine.


About the ES2015 caveats:

The new let, const, and class are very interesting beasties: When used at global scope, they create globals, but they don't create properties on the global object. As of ES2015, although all properties of the global object are globals, not all globals are properties of the global object. It's all part of trying to rein in vastly-polluted global namespace and also bring greater security to the JavaScript binding model. (Now that we have true modules.)

So (note that this will only run in cutting-edge browsers):

// Global scope, in a browser (because I used `window` and `document.body`) that
// implements this aspect of ES2015 (as I write this, Firefox's SpiderMonkey
// doesn't, Chrome's V8 does on the latest Chrome; expect SpiderMonkey and IE
// to catch up pretty quick (didn't test IE Edge, maybe it's already there)

// Strict mode isn't required for this behavior, but for the moment V8 only
// supports the block-scoped constructs in strict mode.
"use strict";
let tbody = setup();

// Old-fashioned var: Creates a property on the global object, so
// we get "function, function"
var f1 = function() { /*...*/ };
result("var declaration", typeof f1, typeof window["f1"]);

// Function declaration: Creates a property on the global object, so
// "function, function"
function f2() {}
result("function declaration", typeof f2, typeof window["f2"]);

// `let` declaration: Doesn't create property on global object, so
// "function, undefined"
let f3 = function() { /*...*/ };
result("let declaration", typeof f3, typeof window["f3"]);

// `const` declaration: Doesn't create property on global object, so
// "function, undefined"
const f4 = function() { /*...*/ };
result("const declaration", typeof f4, typeof window["f4"]);

// `class` declaration: Doesn't create property on global object, so
// "function, undefined"
class C1 {}
result("class declaration", typeof C1, typeof window["C1"]);

function setup() {
  document.body.insertAdjacentHTML(
    "beforeend",
    "<table>" +
    "<thead>" +
    "<tr><th>test</th><th>global</th><th>prop</th></tr>" +
    "</thead>" +
    "<tbody></tbody>" +
    "</table>"
  );
  return document.body.querySelector("tbody");
}

function result(label, direct, win) {
  tbody.insertAdjacentHTML(
    "beforeend",
    "<tr><td>" + [label, direct, win].join("</td><td>") + "</td></tr>"
  );
}
body {
  font-family: sans-serif;
}
table {
  border-collapse: collapse;
}
th, td {
  border: 1px solid #ddd;
  padding: 4px 8px;
}

Output on cutting-edge browsers:

+----------------------+------------+-----------+
|         test         |   global   |   prop    |
+----------------------+------------+-----------+
| var declaration      |  function  | function  |
| function declaration |  function  | function  |
| let declaration      |  function  | undefined |
| const declaration    |  function  | undefined |
| class declaration    |  function  | undefined |
+----------------------+------------+-----------+

Note: Some transpilers don't enforce this rigorously, so if you see different results in transpiled code, don't be surprised.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
12

this[func_name] should give you the function.

var myfunc = this[func_name];
myfunc();
Max Schmeling
  • 12,363
  • 14
  • 66
  • 109
  • I just tried that in Firebug and it didn't work. Maybe it behaves differently in a web page? – rmeador Apr 21 '09 at 15:36
  • It depends on where your function is defined. If the function is not defined on 'this' then it won't find it. You may need to try window[func_name] or Some.Other.Object[func_name] – Max Schmeling Apr 21 '09 at 15:38
4

A safe way to do is to sandbox the alleged function while testing its type:

function isFunction(expr) {
    function sandboxTemplate() {
        var window, document, alert; // etc.

        try {
            return typeof $expr$ == "function";
        } catch (e) {
            return false;
        }
    }

    try {
        var sandbox = new Function(
            sandboxTemplate.toString().replace("$expr$", expr)
            + "return sandboxTemplate()");
        return sandbox();
    } catch (e) {
        return false;
    }
}

function test(expr) {
    document.write("<div>\"" + expr + "\" <b>is "
        + (isFunction(expr) ? "" : "not ")
        + "</b>a function</div>");
}

/* Let's do some testing */

function realFunction() {
}

test("realFunction");       // exists!
test("notHere");            // non-existent
test("alert('Malicious')"); // attempt to execute malicious code!
test("syntax error {");     // attempt to blow us up!

The output:

  • "realFunction" is a function
  • "notHere" is not a function
  • "alert('Malicious')" is not a function
  • "syntax error {" is not a function

The sandboxing code could be written in a more concise manner but I like using "template" functions instead of embedding JS code as string literals.

And oh, this does it nicely without using eval -- though one can argue that using a Function constructor is no different than an eval.

Ates Goral
  • 137,716
  • 26
  • 137
  • 190
3

Use eval:

myFunction = function(){};

var func_name = "myFunction";
var myFunctionPtr = eval(func_name);
Jon Benedicto
  • 10,492
  • 3
  • 28
  • 30
  • http://stackoverflow.com/questions/86513/why-is-using-javascript-eval-function-a-bad-idea – Miles Apr 21 '09 at 17:14
  • 7
    thanks for the link. as with all programming languages, libraries and methods, it can be abused. The key is using it correctly, and knowing what one is doing. If those conditions are met, it's the right tool for the job. – Jon Benedicto Apr 21 '09 at 17:18
  • The thing with eval is that when it seems like a viable solution, 99.9% of the time, it's an indicator that you're approaching the problem in the wrong way. In this case, it's unlikely that the OP really should be getting a function in the current scope by name; instead, the functions should probably be stored in an Object so that they can be directly looked up by key. – Miles Apr 21 '09 at 17:45
  • I get your point - but I wouldn't let that make eval "evil" :-) just in this instance, the OP should probably think up a better design. – Jon Benedicto Apr 21 '09 at 22:40
0

found the function and then call them

autoCallBack : function(_action){
            $(".module").each(function(){
                var modulName = $(this).attr("id");
                if( isFunction(modulName) ){
                    eval(modulName)();
                }
            });
        }

isFunction : function(_functionName){
        try {
            eval(_functionName);
        } catch (error) {
            return false;
        }
    return true;
}
Majid Golshadi
  • 2,686
  • 2
  • 20
  • 29
0

For NodeJs

Write your functions in a separate file and export them and use with name reference of that to call them, Like

//    functions.js
var funcOne = function(){
                   console.log('function ONE called')
              }
module.exports={
    //  name_exported : internal_name
    funcOne : funcOne
}

Use function defined in functions.js in index.js :

//    index.js
var methods = require('./functions.js')   // path to functions.js
methods['funcOne']()

OUTPUT :

> node index.js
> function ONE called
Rupinder Singh
  • 233
  • 4
  • 9
0

I would do something like that.

function yourFunction() { ... }

const yourFunctionName = yourFunction.name

if(typeof this[yourFunctionName] == 'function') {
    const copyOfYourFunction = this[yourFunctionName]
}


copyOfYourFunction() //this works. 

  1. You can get name of function by its name parameter.

  2. this would work better than window because of possible scoping.

0

You can achieve this by using eval (which, in general is not recommended, so if you go this route, make sure you know what you are doing) that can be tested with the first select or, if you know its parent, you can get it inside square brackets. Basically execute and execute2 are loading the function itself, executes it to show it runs successfully and returns the object which can be called later as well.

function foo() {
    alert("foo");
}

function bar() {
    alert("bar");
}

function lorem() {
    alert("lorem");
}

function execute(val) {
    let func = eval(val);
    func();
    return func;
}

function execute2(parent, val) {
    let func = parent[val];
    func();
    return func;
}
<select onchange="execute(this.value);">
    <option value="foo">foo</option>
    <option value="bar">bar</option>
    <option value="lorem">lorem</option>
</select>

<select onchange="execute2(window, this.value);">
    <option value="foo">foo</option>
    <option value="bar">bar</option>
    <option value="lorem">lorem</option>
</select>
Lajos Arpad
  • 64,414
  • 37
  • 100
  • 175