1267

I have the name of a function in JavaScript as a string. How do I convert that into a function pointer so I can call it later?

Depending on the circumstances, I may need to pass various arguments into the method too.

Some of the functions may take the form of namespace.namespace.function(args[...]).

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
Kieron
  • 26,748
  • 16
  • 78
  • 122

37 Answers37

1651

Don't use eval unless you absolutely, positively have no other choice.

As has been mentioned, using something like this would be the best way to do it:

window["functionName"](arguments);

That, however, will not work with a namespace'd function:

window["My.Namespace.functionName"](arguments); // fail

This is how you would do that:

window["My"]["Namespace"]["functionName"](arguments); // succeeds

In order to make that easier and provide some flexibility, here is a convenience function:

function executeFunctionByName(functionName, context /*, args */) {
  var args = Array.prototype.slice.call(arguments, 2);
  var namespaces = functionName.split(".");
  var func = namespaces.pop();
  for(var i = 0; i < namespaces.length; i++) {
    context = context[namespaces[i]];
  }
  return context[func].apply(context, args);
}

You would call it like so:

executeFunctionByName("My.Namespace.functionName", window, arguments);

Note, you can pass in whatever context you want, so this would do the same as above:

executeFunctionByName("Namespace.functionName", My, arguments);
Liam
  • 27,717
  • 28
  • 128
  • 190
Jason Bunting
  • 58,249
  • 14
  • 102
  • 93
  • 4
    you know you don't need the whole "func" construct? "context.apply" alone is fine – annakata Dec 11 '08 at 16:36
  • 21
    Sure, I know that - but the way I wrote the function provides some clarity for those reading it that may not completely grok what is happening. I wrote this function realizing people reading it may need some help. I will provide an alternate though, since you asked... – Jason Bunting Dec 11 '08 at 16:50
  • 129
    Scratch that - the code is clear enough and those that know, know. If you are like me, and know what you are doing, you can just make such changes on your own if you used this code. Stack Overflow is for educating others, and I think my code is easier for the novice to understand. Thanks though! – Jason Bunting Dec 11 '08 at 17:21
  • 4
    Is there a situation when window["funcName"] would return undefined? That is the problem I'm having at the moment. The calling code and the function are defined in two separate js files. I tried adding them to the same file but that made no difference. – codemonkey Mar 16 '10 at 11:19
  • 2
    The `executeFunctionByName` only works if you put it into the global scope. If you don't, you'll get `Uncaught TypeError: Illegal invocation`. I think the last line `return context[func].apply(this, args);` should be `return context[func].apply(window, args);` – meze Mar 25 '11 at 00:08
  • 6
    I think there's a problem here. When you call `My.Namespace.functionName()`, `this` will refer to the `My.Namespace` object. But when you call `executeFunctionByName("My.Namespace.functionName", window)`, there's no way to get `this` to refer to the same thing. Maybe it should use the last namespace as the scope, or `window` if there are no namespaces. Or you could allow the user to specify the scope as an argument. – JW. Jul 06 '11 at 23:08
  • Excellent. Only question - is there a way to make the function exists (i.e. is callable) within executeFunctionByName()? – Andy Sep 03 '11 at 15:45
  • 1
    i'm not able to pass multiple arguments to a function - something like `executeFunctionByName("testFunction", window, new Array("false","Data loaded!"));` is not working - can someone help me find out what i'm doing wrong? - what happens is that both the elements in the array are passed into the first argument of the function testFunction. – Arvind Sridharan Sep 20 '12 at 09:20
  • Is it possible to pass in the 'new' keyword for instantiating objects by name? – flynfish Feb 13 '13 at 23:40
  • `Uncaught TypeError: Object [object global] has no method 'x'` – Brock Hensley Jun 24 '13 at 03:12
  • I tried to use this version but it always threw Illegal Invocation. The @AlexNazarov variation (context as a param for apply) works like a charm. – DNax Oct 03 '13 at 22:53
  • is the variable `arguments` global then? – Paranoid Android Dec 19 '13 at 16:41
  • 1
    i can't get this to work (i keep getting cannot read of unefined. Can someone that has it working post a fiddle? – Sinaesthetic Apr 21 '14 at 19:09
  • Does not work with IE8 and below. Use Alex Nazarov's answer if you need support for old IE browsers. – ajbeaven May 26 '14 at 02:14
  • This solution [doesn't work at all](http://jsfiddle.net/brentonstrine/ykwrmkLc/). Or am I missing something? – brentonstrine Aug 06 '14 at 22:37
  • 2
    @brentonstrine try again without wrapping the javascript code with onload: http://jsfiddle.net/pgpLmdwn/ – maxgalbu Aug 20 '14 at 15:03
  • 3
    In javascript 1.8 you can just do `functionName.split('.').reduce(function(obj, key){ return obj[key]; }, window);` – backus Sep 25 '14 at 05:29
  • 1
    Just pay attention if you're minifying your JS. You might want to create a map of function names => functions if you want this to work in minified production code. – Abdo Apr 25 '15 at 12:32
  • 1
    Unfortunately, this answer is flawed because it doesn't set the execution context correctly. It's a pity that this hasn't been fixed here in all these years. Please use the answer of @AlexNazarov below, he gets it right. – hashchange Oct 30 '15 at 09:16
  • 1
    I had to uncomment the `args` parameter and then override its value on the first line so that TypeScript didn't freak out about the call not matching the function signature. – alan May 23 '17 at 21:46
  • Makes sense, @alan - TypeScript is somewhat strict (which is why I am loathe to use it). ;) – Jason Bunting May 24 '17 at 20:20
  • There's a lot going in this function that can be done with single line of code. @Backus I was here to give that exact line of code and saw you already put it here. Cheers. I have put a simple function as new answer for those who are looking for solution with fallback in case of null string or function. – SJ00 Jun 02 '18 at 19:27
  • Any one suggest me on this. functionName = 'function myFunction(argument) {alert('My function ' + argument);}'; window[myFunction]('is working.'); is it possible to execute a method like above? – Cegone Oct 10 '19 at 09:12
  • I found that I had to define the function in a particular way for window['myFn'] or MyNamespace['myFn'} to work. Arrow functions didn't work. myFn = function() {...} worked for global, and MyNamespace.myFn = function myFn() {...} worked for namespaced. – Little Brain Mar 02 '20 at 15:52
  • @LittleBrain - this question & answer are rather old - arrow functions were non-existent at the time... – Jason Bunting Mar 10 '20 at 16:34
  • 1
    Understood, I just want to help anybody else who comes across this now. – Little Brain Mar 10 '20 at 18:51
  • What if the function you want to call is an import? – geoidesic Mar 19 '20 at 09:55
  • This solution does not work, if the script is of type="module". Don't ask me why though... Example: `` works, whereas `` will not work. – René K May 20 '20 at 16:31
  • I am referring to my comment above: Reason for it not working: "In a module context, variables don't automatically get declared globally." https://stackoverflow.com/a/49338383/5263954 – René K May 20 '20 at 16:41
  • It seems that no one took care of the `event` variable yet. I tried to push it to the end of `args` and it worked, but it was corrupt - returning 0 or the same number again and again (can't explain but tried out with the original `eventListener`, which worked correctly). So the `call` method would be probably the best way to fix it, but how can you pass the array as arguments to it, after the event? –  Mar 12 '21 at 21:31
  • how to do it in node JS ? in nodeJS `window` is not defined – aakash4dev Apr 23 '22 at 10:49
  • @aakash4dev - I don't really know NodeJS, but I believe "global" is the name of the global context? So try that. That's all "window" is here, just the name of the local, global context. ;) – Jason Bunting Apr 27 '22 at 03:40
  • why "args" is commented? – serge Sep 13 '22 at 13:20
111

Just thought I'd post a slightly altered version of Jason Bunting's very helpful function.

First, I have simplified the first statement by supplying a second parameter to slice(). The original version was working fine in all browsers except IE.

Second, I have replaced this with context in the return statement; otherwise, this was always pointing to window when the target function was being executed.

function executeFunctionByName(functionName, context /*, args */) {
    var args = Array.prototype.slice.call(arguments, 2);
    var namespaces = functionName.split(".");
    var func = namespaces.pop();
    for (var i = 0; i < namespaces.length; i++) {
        context = context[namespaces[i]];
    }
    return context[func].apply(context, args);
}
Community
  • 1
  • 1
Alex Nazarov
  • 1,285
  • 1
  • 8
  • 6
83

The answer to this other question shows you how to do that: Javascript equivalent of Python's locals()?

Basically, you can say

window["foo"](arg1, arg2);

or as many others have suggested, you can just use eval:

eval(fname)(arg1, arg2);

although this is extremely unsafe unless you're absolutely sure about what you're eval-ing.

Community
  • 1
  • 1
Eli Courtwright
  • 186,300
  • 67
  • 213
  • 256
82

I think an elegant way of doing this is by defining your functions in a hash object. Then you can have a reference to those functions from the hash using the string. e.g.

var customObject = {
  customFunction: function(param){...}
};

Then you can call:

customObject['customFunction'](param);

Where customFunction will be a string matching a function defined in your object.

UPDATE

It seems that this answer was helpful for many fellow coders out there so here goes an updated version.

With ES6 you can additionally use Computed Property Names which will allow you to avoid magic strings.

const FunctionNames = Object.freeze({ 
  FirstFunction: "firstFunction", 
  SecondFunction: "secondFunction" 
});

...

var customObject = {
  [FunctionNames.FirstFunction]: function(param){...},
  [FunctionNames.SecondFunction]: function(param){...}
};

...

customObject[FunctionNames.FirstFunction](param);
Ruben Daddario
  • 1,013
  • 8
  • 9
  • @ibsenv, thank you for your comment to help me identify this response as the best. I created an array of function objects and in turn used that to create an array of deferred.promises. I put some sample code below. (I did not want to create a new reply and *borrow* Ruben's response.) – user216661 Jan 08 '16 at 16:55
  • function getMyData(arrayOfObjectsWithIds) { var functionArray = arrayOfObjectsWithIds.map( function (value) { return {myGetDataFunction: MyService.getMyData(value.id)}; }) var promises = functionArray.map( function (getDataFunction) { var deferred =$q.defer(); getDataFunction.myGetDataFunction.success( function(data) { deferred.resolve(data) }). error( function (error) { deferred.reject(); }); return deferred.promise; }); $q.all(promises).then( function (dataArray) { //do stuff }) }; – user216661 Jan 08 '16 at 16:58
  • This works excellent I only add underscore/lodash for verify if its a function. And then run – elporfirio Sep 21 '16 at 16:18
63

Could you not just do this:

var codeToExecute = "My.Namespace.functionName()";
var tmpFunc = new Function(codeToExecute);
tmpFunc();

You can also execute any other JavaScript using this method.

Coley
  • 707
  • 6
  • 6
61

With ES6 you could to access class methods by name:

class X {
  method1(){
    console.log("1");
  }
  method2(){
    this['method1']();
    console.log("2");
  }
}
let x  = new X();
x['method2']();

the output would be:

1
2
nils petersohn
  • 2,317
  • 2
  • 25
  • 27
  • 2
    Best javascript **PURE** ... God.. delete class not working and but its ok. Thanks! – KingRider Jun 03 '16 at 13:51
  • 2
    This is the thing i was looking for from a long time. Thanks! – PaladiN May 22 '17 at 12:54
  • 1
    ES2015 has nothing to do here. You can achieve the same goal using pure objects, or prototype delegation via `Object.create()`. const myObj = { method1() { console.log('1') }, method2() { console.log('2') } } myObj['method1'](); // 1 myObj['method2'](); // 2 – sminutoli Oct 26 '18 at 02:54
  • 1
    This is gold!!! I am surprised I have never thought of this before. Nice!!! – thxmike Aug 19 '19 at 01:39
  • I also think this is the neatest way to achieve our goal. – Chris Jung Sep 05 '19 at 15:50
  • Must say, this is indeed way safer than attempting to do an `eval()` or anything of the sort. And simple too. You can explicitly decide what things do. Nice solution! – Pablo Alexis Domínguez Grau Nov 09 '20 at 22:24
  • If you want to use a variable, e.g. let y = 'method2'; x[y]();, you have to declare an index type for class X {[key: string]: any;… otherwise you'll get an error: »Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'X'. No index signature with a parameter of type 'string' was found on type 'X'.« – Denis Giffeler Mar 07 '22 at 17:52
30

Two things:

  • avoid eval, it's terribly dangerous and slow

  • secondly it doesn't matter where your function exists, "global" -ness is irrelevant. x.y.foo() can be enabled through x.y['foo']() or x['y']['foo']() or even window['x']['y']['foo'](). You can chain indefinitely like this.

annakata
  • 74,572
  • 17
  • 113
  • 180
28

All the answers assume that the functions can be accessed through global scope (window). However, the OP did not make this assumption.

If the functions live in a local scope (aka closure) and are not referenced by some other local object, bad luck: You have to use eval() AFAIK, see dynamically call local function in javascript

Wolfgang Kuehn
  • 12,206
  • 2
  • 33
  • 46
  • 4
    Dude (or dudette), thank you so much for pointing that out! I thought I was going crazy for a second. – Funktr0n Mar 16 '14 at 03:13
  • If it is in local object you can just do : localobject['function_name'](), same as window['function_name']() – Kyobul Nov 28 '21 at 09:55
24

Depending on where you are you can also use:

this["funcname"]();
self["funcname"]();
window["funcname"]();
top["funcname"]();
globalThis["funcname"]();

or, in nodejs

global["funcname"]()
Zibri
  • 9,096
  • 3
  • 52
  • 44
  • Thank this answer it's possible do `function callObjectMethod(obj,meth){ return (_v) => { obj[meth](_v) } }`. For me this is useful to call some object method with a parameter coming via callback from external service. Hope this help someone else. – Enrique René Dec 18 '20 at 19:27
15

Here is my contribution to Jason Bunting's / Alex Nazarov's excellent answers, where I include error checking requested by Crashalot.

Given this (contrived) preamble:

a = function( args ) {
    console.log( 'global func passed:' );
    for( var i = 0; i < arguments.length; i++ ) {
        console.log( '-> ' + arguments[ i ] );
    }
};
ns = {};
ns.a = function( args ) {
    console.log( 'namespace func passed:' );
    for( var i = 0; i < arguments.length; i++ ) {
        console.log( '-> ' + arguments[ i ] ); 
    }
};
name = 'nsa';
n_s_a = [ 'Snowden' ];
noSuchAgency = function(){};

then the following function:

function executeFunctionByName( functionName, context /*, args */ ) {
    var args, namespaces, func;

    if( typeof functionName === 'undefined' ) { throw 'function name not specified'; }

    if( typeof eval( functionName ) !== 'function' ) { throw functionName + ' is not a function'; }

    if( typeof context !== 'undefined' ) { 
        if( typeof context === 'object' && context instanceof Array === false ) { 
            if( typeof context[ functionName ] !== 'function' ) {
                throw context + '.' + functionName + ' is not a function';
            }
            args = Array.prototype.slice.call( arguments, 2 );

        } else {
            args = Array.prototype.slice.call( arguments, 1 );
            context = window;
        }

    } else {
        context = window;
    }

    namespaces = functionName.split( "." );
    func = namespaces.pop();

    for( var i = 0; i < namespaces.length; i++ ) {
        context = context[ namespaces[ i ] ];
    }

    return context[ func ].apply( context, args );
}

will allow you to call a javascript function by name stored in a string, either namespaced or global, with or without arguments (including Array objects), providing feedback on any errors encountered (hopefully catching them).

The sample output shows how it works:

// calling a global function without parms
executeFunctionByName( 'a' );
  /* OUTPUT:
  global func passed:
  */

// calling a global function passing a number (with implicit window context)
executeFunctionByName( 'a', 123 );
  /* OUTPUT:
  global func passed:
  -> 123
  */

// calling a namespaced function without parms
executeFunctionByName( 'ns.a' );
  /* OUTPUT:
  namespace func passed:
  */

// calling a namespaced function passing a string literal
executeFunctionByName( 'ns.a', 'No Such Agency!' );
  /* OUTPUT:
  namespace func passed:
  -> No Such Agency!
  */

// calling a namespaced function, with explicit context as separate arg, passing a string literal and array 
executeFunctionByName( 'a', ns, 'No Such Agency!', [ 007, 'is the man' ] );
  /* OUTPUT:
  namespace func passed:
  -> No Such Agency!
  -> 7,is the man
  */

// calling a global function passing a string variable (with implicit window context)
executeFunctionByName( 'a', name );
  /* OUTPUT:
  global func passed:
  -> nsa
  */

// calling a non-existing function via string literal
executeFunctionByName( 'n_s_a' );
  /* OUTPUT:
  Uncaught n_s_a is not a function
  */

// calling a non-existing function by string variable
executeFunctionByName( n_s_a );
  /* OUTPUT:
  Uncaught Snowden is not a function
  */

// calling an existing function with the wrong namespace reference
executeFunctionByName( 'a', {} );
  /* OUTPUT:
  Uncaught [object Object].a is not a function
  */

// calling no function
executeFunctionByName();
  /* OUTPUT:
  Uncaught function name not specified
  */

// calling by empty string
executeFunctionByName( '' );
  /* OUTPUT:
  Uncaught  is not a function
  */

// calling an existing global function with a namespace reference
executeFunctionByName( 'noSuchAgency', ns );
  /* OUTPUT:
  Uncaught [object Object].noSuchAgency is not a function
  */
Mac
  • 1,432
  • 21
  • 27
  • Dunno...it's a very good effort, that clear. But sounds like "too broad" to me... – Niki Romagnoli Mar 29 '17 at 09:33
  • 2
    Huh? SO is an question/answer/teaching platform. I'll gladly provide all the examples I can think of to hopefully convey illumination. To me, _that's the point_. – Mac Dec 08 '17 at 18:18
  • If you're eval'ing the functionName anyway, why not just use that? – data Sep 27 '18 at 08:46
  • This doesn't work for me. I have a namespaced function a.b.c.d where d is the function name. the call executeFunctionByName("a.b.c.d", window) fails on line that checks `if( typeof context[ functionName ] !== 'function' )` because context - window - is defined, is an object and an array, but window['a.b.c.d'] doesn't exist as was identified as a problem in the accepted answer: `window["My.Namespace.functionName"](arguments); // fail` – akousmata May 09 '19 at 12:39
14

You just need convert your string to a pointer by window[<method name>]. example:

var function_name = "string";
function_name = window[function_name];

and now you can use it like a pointer.

Rob W
  • 341,306
  • 83
  • 791
  • 678
Amirali
  • 2,436
  • 1
  • 16
  • 4
12

Here is my Es6 approach which enables you to call your function by it's name as string or it's function name and also enable you to pass different numbers of arguments to different types of functions:

function fnCall(fn, ...args)
{
  let func = (typeof fn =="string")?window[fn]:fn;
  if (typeof func == "function") func(...args);
  else throw new Error(`${fn} is Not a function!`);
}


function example1(arg1){console.log(arg1)}
function example2(arg1, arg2){console.log(arg1 + "  and   " + arg2)}
function example3(){console.log("No arguments!")}

fnCall("example1", "test_1");
fnCall("example2", "test_2", "test3");
fnCall(example3);
fnCall("example4"); // should raise an error in console
Gershom Maes
  • 7,358
  • 2
  • 35
  • 55
pouyan
  • 3,445
  • 4
  • 26
  • 44
11

If you want to call a function of an object instead of a global function with window["functionName"]. You can do it like;

var myObject=new Object();
myObject["functionName"](arguments);

Example:

var now=new Date();
now["getFullYear"]()
Ahmet DAL
  • 4,445
  • 9
  • 47
  • 71
11

BE CAREFUL!!!

One should try to avoid calling a function by string in JavaScript for two reasons:

Reason 1: Some code obfuscators will wreck your code as they will change the function names, making the string invalid.

Reason 2: It is much harder to maintain code that uses this methodology as it is much harder to locate usages of the methods called by a string.

dykstrad
  • 398
  • 3
  • 10
  • Your answer convinced me to stop scrolling between answers and drop down the idea of calling a function using its name as a string, thank you, sir. – Yuniac May 16 '22 at 12:50
9

I don't think you need complicated intermediate functions or eval or be dependent on global variables like window:

function fun1(arg) {
  console.log(arg);
}

function fun2(arg) {
  console.log(arg);
}

const operations = {
  fun1,
  fun2
};

operations["fun1"]("Hello World");
operations.fun2("Hello World");

// You can use intermediate variables, if you like
let temp = "fun1";
operations[temp]("Hello World");

It will also work with imported functions:

// mode.js
export function fun1(arg) {
  console.log(arg);
}

export function fun2(arg) {
  console.log(arg);
}
// index.js
import { fun1, fun2 } from "./mod";

const operations = {
  fun1,
  fun2
};

operations["fun1"]("Hello World");
operations["fun2"]("Hello World");

Since it is using property access, it will survive minimization or obfuscation, contrary to some answers you will find here.

snnsnn
  • 10,486
  • 4
  • 39
  • 44
7

Surprised to see no mention of setTimeout.

To run a function without arguments:

var functionWithoutArguments = function(){
    console.log("Executing functionWithoutArguments");
}
setTimeout("functionWithoutArguments()", 0);

To run function with arguments:

var functionWithArguments = function(arg1, arg2) {
    console.log("Executing functionWithArguments", arg1, arg2);
}
setTimeout("functionWithArguments(10, 20)");

To run deeply namespaced function:

var _very = {
    _deeply: {
        _defined: {
            _function: function(num1, num2) {
                console.log("Execution _very _deeply _defined _function : ", num1, num2);
            }
        }
    }
}
setTimeout("_very._deeply._defined._function(40,50)", 0);
abhishekisnot
  • 113
  • 2
  • 8
  • This does not provide an answer to the question. To critique or request clarification from an author, leave a comment below their post - you can always comment on your own posts, and once you have sufficient [reputation](http://stackoverflow.com/help/whats-reputation) you will be able to [comment on any post](http://stackoverflow.com/help/privileges/comment). – AstroCB Oct 17 '14 at 20:57
  • Please add an example of how would you call `runMe` with a few arguments. – lexicore Oct 17 '14 at 21:01
  • You can call it like any other function. Just that it will be a string. `var functionName = "addNumbers(10, 20)"; setTimeout(functionName,10);` – abhishekisnot Oct 17 '14 at 21:16
  • @abhishekisnot Next time it would be better to provide a full answer, not just a short snippet. The question addresses `run.run.runMe`, not just `runMe` as well as arguments. Please accept this as a friendly feedback. – lexicore Oct 17 '14 at 21:19
  • @AstroCB This _does_ provide a rudimentary answer to the question. It is therefore more appropriate to give correcting feedback here rather than vote for closing. – lexicore Oct 17 '14 at 21:21
  • 1
    @lexicore I voted for *deletion* in a review queue, because it does not **clearly** provide a substantial answer to the question and it is of little value on its own. – AstroCB Oct 17 '14 at 21:22
  • @lexicore thank you for feedback. It was my mistake. I assumed the answer was too trivial to provide example. Edited the answer. – abhishekisnot Oct 17 '14 at 21:41
  • 1
    This method has potentially huge flaw, as it puts execution to **end of rendering** queue, thus making this call **asynchronious** – PeterM Nov 15 '16 at 10:27
  • 2
    I like this answer, it seems to work for my requirements. – Quintonn Sep 18 '18 at 09:27
3

So, like others said, definitely the best option is:

window['myfunction'](arguments)

And like Jason Bunting said, it won't work if the name of your function includes an object:

window['myobject.myfunction'](arguments); // won't work
window['myobject']['myfunction'](arguments); // will work

So here's my version of a function that will execute all functions by name (including an object or not):

my = {
    code : {
        is : {
            nice : function(a, b){ alert(a + "," + b); }
        }
    }
};

guy = function(){ alert('awesome'); }

function executeFunctionByName(str, args)
{
    var arr = str.split('.');
    var fn = window[ arr[0] ];
    
    for (var i = 1; i < arr.length; i++)
    { fn = fn[ arr[i] ]; }
    fn.apply(window, args);
}

executeFunctionByName('my.code.is.nice', ['arg1', 'arg2']);
executeFunctionByName('guy');
Community
  • 1
  • 1
pmrotule
  • 9,065
  • 4
  • 50
  • 58
3
  let t0 = () => { alert('red0') }
  var t1 = () =>{ alert('red1') }
  var t2 = () =>{ alert('red2') }
  var t3 = () =>{ alert('red3') }
  var t4 = () =>{ alert('red4') }
  var t5 = () =>{ alert('red5') }
  var t6 = () =>{ alert('red6') }

  function getSelection(type) {
    var evalSelection = {
      'title0': t0,
      'title1': t1,
      'title2': t2,
      'title3': t3,
      'title4': t4,
      'title5': t5,
      'title6': t6,
      'default': function() {
        return 'Default';
      }
    };
    return (evalSelection[type] || evalSelection['default'])();
  }
  getSelection('title1');

A more OOP solution ...

Leo Lanese
  • 476
  • 4
  • 5
2

One more detail on Jason and Alex's posts. I found it helpful to add a default value to context. Just put context = context == undefined? window:context; at the beginning of the function. You can change window to whatever your preferred context is, and then you won't need to pass in the same variable each time you call this in your default context.

2

To add to Jason Bunting's answer, if you're using nodejs or something (and this works in dom js, too), you could use this instead of window (and remember: eval is evil:

this['fun'+'ctionName']();
Cilan
  • 13,101
  • 3
  • 34
  • 51
2

There's a very similar thing in my code. I have a server-generated string which contains a function name which I need to pass as a callback for a 3rd party library. So I have a code that takes the string and returns a "pointer" to the function, or null if it isn't found.

My solution was very similar to "Jason Bunting's very helpful function" *, although it doesn't auto-execute, and the context is always on the window. But this can be easily modified.

Hopefully this will be helpful to someone.

/**
 * Converts a string containing a function or object method name to a function pointer.
 * @param  string   func
 * @return function
 */
function getFuncFromString(func) {
    // if already a function, return
    if (typeof func === 'function') return func;

    // if string, try to find function or method of object (of "obj.func" format)
    if (typeof func === 'string') {
        if (!func.length) return null;
        var target = window;
        var func = func.split('.');
        while (func.length) {
            var ns = func.shift();
            if (typeof target[ns] === 'undefined') return null;
            target = target[ns];
        }
        if (typeof target === 'function') return target;
    }

    // return null if could not parse
    return null;
}
Community
  • 1
  • 1
nomæd
  • 421
  • 5
  • 13
2

Here's a bit robust and reusable solution I ended up implementing for one of my projects.

A FunctionExecutor Constructor Function

Usage:

let executor = new FunctionExecutor();
executor.addFunction(two)
executor.addFunction(three)

executor.execute("one");
executor.execute("three");

Obviously in the project the adding of all the functions that required to be called by name was done by a loop.

The function Executor:

function FunctionExecutor() {
  this.functions = {};

  this.addFunction = function (fn) {
    let fnName = fn.name;
    this.functions[fnName] = fn;
  }

  this.execute = function execute(fnName, ...args) {
    if (fnName in this.functions && typeof this.functions[fnName] === "function") {
      return this.functions[fnName](...args);
    }
    else {
      console.log("could not find " + fnName + " function");
    }
  }

  this.logFunctions = function () {
    console.log(this.functions);
  }
}

Example Usage:

function two() {
  console.log("two"); 
}

function three() {
  console.log("three");
}

let executor = new FunctionExecutor();
executor.addFunction(two)
executor.addFunction(three)

executor.execute("one");
executor.execute("three");
vivek agarwal
  • 435
  • 4
  • 6
2
  const myFnCollection = {
    myFnStringName: function(args) {}
  };

  let fn = 'myFnStringName';

  // 1. Recommended
  if (typeof window[fn] === 'function') {
    window[fn](args);
  }

  // 2. Recommended
  if (typeof myFnCollection[fn] === 'function') {
    myFnCollection[fn](args);
  }

  // 3. Eval is evil ;)
  if (typeof eval(fn) === 'function') {
    eval(fn)(args);
  }
Gigoland
  • 1,287
  • 13
  • 10
1

There too some very helpful way.

http://devlicio.us/blogs/sergio_pereira/archive/2009/02/09/javascript-5-ways-to-call-a-function.aspx

var arrayMaker = {  
    someProperty: 'some value here',  
    make: function (arg1, arg2) {  
        return [ this, arg1, arg2 ];  
    },
    execute: function_name
};
merqlove
  • 3,674
  • 1
  • 23
  • 22
1

I can't resist mentioning another trick, which helps if you have an unknown number of arguments that are also being passed as part of the string containing the function name. For example:

var annoyingstring = 'call_my_func(123, true, "blah")';

If your Javascript is running on a HTML page, all you need is an invisible link; you can pass a string into the onclick attribute, and the call the click method.

<a href="#" id="link_secret"><!-- invisible --></a>

$('#link_secret').attr('onclick', annoyingstring);
$('#link_secret').click();

Or create the <a> element at runtime.

Magnus Smith
  • 5,895
  • 7
  • 43
  • 64
1

Easiest way is to access it like has element

window.ClientSideValidations.forms.location_form

is same as

window.ClientSideValidations.forms['location_form']
crazycrv
  • 2,395
  • 2
  • 19
  • 20
1

People keep saying that eval is dangerous and evil because it can run any arbitrary code. However, if you use eval with a whitelisting approach, assuming you know all the possible function names that may need to be run in advance, then eval is no longer a security concern because the input is no longer arbitrary. Whitelisting is a good and frequent security pattern. Here's an example:

function runDynamicFn(fnName, ...args) {
  // can also be fed from a tightly controlled config
  const allowedFnNames = ['fn1', 'ns1.ns2.fn3', 'ns4.fn4'];

  return allowedFnNames.includes(fnName) ? eval(fnName)(...args) : undefined; 
}

// test function:
function fn1(a) { 
  console.log('fn1 called with', a)
}

runDynamicFn('alert("got you!")')
runDynamicFn('fn1', 'foo')
SimoAmi
  • 1,696
  • 14
  • 13
  • This is still a poor implementation in my opinion; it would be better to map the functions: `let allowedFns = new Map(); allowedFns.set('fn1', fn1); allowedFns.set('ns1.ns2.fn3', ns1.ns2.fn3); ...`. If the usage of `eval` is safe, the problem can probably be solved without `eval` :-P – Gershom Maes Apr 21 '20 at 15:32
  • If you know all the function names, why not just create an array with the functions? – Danial Aug 30 '20 at 16:11
1

I like concise solutions, so come up with this more ES6 way:

const executeByName = (name, originContext, ...args) => {
  const namespaces = name.split('.');
  const func = namespaces.pop();
  const funcContext = namespaces.reduce((context, namespace) => context[namespace], originContext);
  return funcContext[func](...args);
};
catcher
  • 101
  • 1
  • 5
0

This is working for me:

var command = "Add";
var tempFunction = new Function("Arg1","Arg2", "window." + command + "(Arg1,Arg2)");
tempFunction(x,y);

I hope this works.

sharkbait
  • 2,980
  • 16
  • 51
  • 89
DevAshish
  • 11
  • 6
0

Look basic:

var namefunction = 'jspure'; // String

function jspure(msg1 = '', msg2 = '') { 
  console.log(msg1+(msg2!=''?'/'+msg2:''));
} // multiple argument

// Results ur test
window[namefunction]('hello','hello again'); // something...
eval[namefunction] = 'hello'; // use string or something, but its eval just one argument and not exist multiple

Exist other type function is class and look example nils petersohn

KingRider
  • 2,140
  • 25
  • 23
0

Thanks for the very helpful answer. I'm using Jason Bunting's function in my projects.

I extended it to use it with an optional timeout, because the normal way to set a timeout wont work. See abhishekisnot's question

function executeFunctionByName(functionName, context, timeout /*, args */ ) {
 var args = Array.prototype.slice.call(arguments, 3);
 var namespaces = functionName.split(".");
 var func = namespaces.pop();
 for (var i = 0; i < namespaces.length; i++) {
  context = context[namespaces[i]];
 }
 var timeoutID = setTimeout(
  function(){ context[func].apply(context, args)},
  timeout
 );
    return timeoutID;
}

var _very = {
    _deeply: {
        _defined: {
            _function: function(num1, num2) {
                console.log("Execution _very _deeply _defined _function : ", num1, num2);
            }
        }
    }
}

console.log('now wait')
executeFunctionByName("_very._deeply._defined._function", window, 2000, 40, 50 );
Community
  • 1
  • 1
Neo
  • 1
  • 3
0

There are several executeByName functions here which works fine, unless name contains square brackets - issue I ran into - as I have dynamically generated names. So above functions will fail on names like

app.widget['872LfCHc']['toggleFolders']

As a remedy, I've made function to take this into account too, maybe someone will find it usefull:

Generated from CoffeeScript:

var executeByName = function(name, context) {
  var args, func, i, j, k, len, len1, n, normalizedName, ns;
  if (context == null) {
    context = window;
  }
  args = Array.prototype.slice.call(arguments, 2);
  normalizedName = name.replace(/[\]'"]/g, '').replace(/\[/g, '.');
  ns = normalizedName.split(".");
  func = context;
  for (i = j = 0, len = ns.length; j < len; i = ++j) {
    n = ns[i];
    func = func[n];
  }
  ns.pop();
  for (i = k = 0, len1 = ns.length; k < len1; i = ++k) {
    n = ns[i];
    context = context[n];
  }
  if (typeof func !== 'function') {
    throw new TypeError('Cannot execute function ' + name);
  }
  return func.apply(context, args);
}

For better readability check also CoffeeScript version:

executeByName = (name, context = window) ->
    args = Array.prototype.slice.call(arguments, 2)
    normalizedName = name.replace(/[\]'"]/g, '').replace(/\[/g, '.')
    ns = normalizedName.split "."
    func = context
    for n, i in ns
        func = func[n]

    ns.pop()
    for n, i in ns
        context = context[n];
    if typeof func != 'function'
        throw new TypeError 'Cannot execute function ' + name
    func.apply(context, args)
PeterM
  • 1,478
  • 1
  • 22
  • 28
0

You can call javascript function within the eval("functionname as string") either. Like below: (eval is pure javascript function)

function testfunc(){
    return "hello world";
}

$( document ).ready(function() {

     $("div").html(eval("testfunc"));
});

Working example: https://jsfiddle.net/suatatan/24ms0fna/4/

Suat Atan PhD
  • 1,152
  • 13
  • 27
0

all you have to do is use a context or define a new context where your function(s) reside. you are not limited to window["f"]();

here is an example of how I use some dynamic invocation for some REST services.

/* 
Author: Hugo Reyes
@ www.teamsrunner.com

*/

    (function ( W, D) { // enclose it as a self-invoking function to avoid name collisions.


    // to call function1 as string
    // initialize your FunctionHUB as your namespace - context
    // you can use W["functionX"](), if you want to call a function at the window scope.
    var container = new FunctionHUB();


    // call a function1 by name with one parameter.
    
    container["function1"](' Hugo ');


    // call a function2 by name.
    container["function2"](' Hugo Leon');
    

    // OO style class
    function FunctionHUB() {

        this.function1 = function (name) {

            console.log('Hi ' + name + ' inside function 1')
        }

        this.function2 = function (name) {

            console.log('Hi' + name + ' inside function 2 ')
        }
    }

})(window, document); // in case you need window context inside your namespace.

If you want to generate the entire function from a string, that's a different answer. also please notice that you are not limited to a single namespace, if your namespace exists as my.name.space.for.functions.etc.etc.etc the last branch of your namespace contains the function as my.name.space.for.functions.etc.etc["function"]();

Hope it helps. H.

Ahmad Adibzad
  • 501
  • 2
  • 6
  • 14
Hugo R
  • 2,613
  • 1
  • 14
  • 6
0

Since eval() is evil, and new Function() is not the most efficient way to achieve this, here is a quick JS function that returns the function from its name in string.

  • Works for namespace'd functions
  • Fallbacks to null-function in case of null/undefined string
  • Fallbacks to null-function if function not found

    function convertStringtoFunction(functionName){

        var nullFunc = function(){}; // Fallback Null-Function
        var ret = window; // Top level namespace

        // If null/undefined string, then return a Null-Function
        if(functionName==null) return nullFunc;

        // Convert string to function name
        functionName.split('.').forEach(function(key){ ret = ret[key]; });

        // If function name is not available, then return a Null-Function else the actual function
        return (ret==null ? nullFunc : ret);

    }

Usage:


    convertStringtoFunction("level1.midLevel.myFunction")(arg1, arg2, ...);

SJ00
  • 638
  • 1
  • 6
  • 10
0

You may place your functions names in an Array object and then call the array key respective to each function to execute it, DEMO:

function captchaTest(msg){
    let x = Math.floor(Math.random()*(21-1)) +1;
    let y = Math.floor(Math.random()*(11-1)) +1;
    let sum = function(){
        return x+y;
    }
    let sub = function(){
        if (y > x){
            let m = y;
            y = x;
            x = m;
            console.log(x,y,m,'--')
        }
        return x-y;
    }
    let mul = function(){
        return x*y;
    } 
    let OParr = [sum(), sub(), mul()]; 
    let OP = Math.floor(Math.random()*OParr.length);      
    let s = OParr[OP]; //!!! HERE !!! is the call as array element
    switch(OP){
        case 0:
            opra = '+';
            break;
        case 1:
            opra = '━';
            break;
        default:
            opra = '✖';
    }
    let msg2 = 'Answer the following question to continue:'
    let p = prompt(msg+' '+msg2+'\n'+'What is the result of '+x+opra+y+' ?','')
    console.log(s,p,OP)
    if (s == p){
        alert ('Wow, Correct Answer!')
        return true;
    }
    else{
        alert('Sorry, the answer is not correct!')
        return false;
    }
}
SaidbakR
  • 13,303
  • 20
  • 101
  • 195
-1

Without using eval('function()') you could to create a new function using new Function(strName). The below code was tested using FF, Chrome, IE.

<html>
<body>
<button onclick="test()">Try it</button>
</body>
</html>
<script type="text/javascript">

  function test() {
    try {    
        var fnName = "myFunction()";
        var fn = new Function(fnName);
        fn();
      } catch (err) {
        console.log("error:"+err.message);
      }
  }

  function myFunction() {
    console.log('Executing myFunction()');
  }

</script>
Ithar
  • 4,865
  • 4
  • 39
  • 40