当我的名字作为字符串时,如何执行JavaScript函数

How to execute a JavaScript function when I have its name as a string

我将javascript中的函数名作为字符串。如何将其转换为函数指针以便稍后调用?

根据具体情况,我可能还需要将各种参数传递到方法中。

有些功能可以采用namespace.namespace.function(args[...])的形式。


不要使用eval,除非你绝对地、肯定地别无选择。

如前所述,使用这样的方法是最好的方法:

1
window["functionName"](arguments);

但是,这不适用于命名空间的d函数:

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

您可以这样做:

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

为了使这更容易并提供一些灵活性,这里有一个方便的功能:

1
2
3
4
5
6
7
8
9
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);
}

你可以这样称呼它:

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

注意,您可以在任何上下文中传递,因此这与上面的操作相同:

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


我只是想发布一个稍微修改过的版本,杰森·邦廷的非常有用的功能。

首先,我通过向slice()提供第二个参数简化了第一条语句。原始版本在除IE以外的所有浏览器中都工作正常。

其次,我在RETURN语句中将其替换为上下文;否则,在执行目标函数时,它总是指向窗口。

1
2
3
4
5
6
7
8
9
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);
}


另一个问题的答案向您展示了如何做到这一点:JavaScript相当于python的locals()?

基本上,你可以说

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

或者像其他许多人建议的那样,您可以使用eval:

1
eval(fname)(arg1, arg2);

虽然这是非常不安全的,除非你完全确定你在逃避什么。


你不能这样做吗:

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

您还可以使用此方法执行任何其他JavaScript。


我认为这样做的一种优雅方式是在散列对象中定义函数。然后可以使用字符串从散列中引用这些函数。例如

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

然后你可以打电话给:

1
customObject['customFunction'](param);

其中,customFunction将是与对象中定义的函数匹配的字符串。


使用ES6,可以按名称访问类方法:

1
2
3
4
5
6
7
8
9
10
11
class X {
  method1(){
    console.log("1");
  }
  method2(){
    this['method1']();
    console.log("2");
  }
}
let x  = new X();
x['method2']();

输出将是:

1
2
1
2


两件事:

  • 避免逃避,这是非常危险和缓慢的。

  • 其次,你的职能在哪里并不重要,"全局性"是不相关的。x.y.foo()可以通过x.y['foo']()x['y']['foo']()甚至window['x']['y']['foo']()启用。你可以像这样无限期地连锁。


所有的答案都假设可以通过全局范围(aka窗口)访问函数。然而,OP并没有做出这样的假设。

如果函数位于本地作用域(亦称闭包)中,并且没有被其他本地对象引用,那么不幸的是:您必须使用eval()afaik,请参见在javascript中动态调用本地函数


您只需要通过window[]将字符串转换为指针即可。例子:

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

现在你可以像指针一样使用它。


以下是我对杰森·邦廷/亚历克斯·纳扎罗夫的优秀答案的贡献,其中包括了Crashalot要求的错误检查。

鉴于此(人为的)序言:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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(){};

然后执行以下功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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 );
}

将允许您通过存储在字符串中的名称调用一个javascript函数,无论是名称空间的还是全局的,无论是否有参数(包括数组对象),对遇到的任何错误提供反馈(希望能够捕捉到它们)。

示例输出显示了它的工作方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
// 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
  */


如果要使用window["functionName"]调用对象的函数,而不是全局函数。你可以像这样做;

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

例子:

1
2
var now=new Date();
now["getFullYear"]()

小心!!!!

应该尽量避免在javascript中按字符串调用函数,原因有两个:

原因1:一些代码模糊器会破坏代码,因为它们会更改函数名,使字符串无效。

原因2:维护使用此方法的代码要困难得多,因为查找字符串调用的方法的用法要困难得多。


根据您所在的位置,您还可以使用:

1
2
3
4
5
this["funcname"]();
self["funcname"]();
window["funcname"]();
top["funcname"]();
globalThis["funcname"]();

或者,在诺迪斯

1
global["funcname"]()

很惊讶没有提到设置时间。

运行不带参数的函数:

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

要用参数运行函数:

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

要运行深度命名函数:

1
2
3
4
5
6
7
8
9
10
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);


下面是我的ES6方法,它允许您以字符串或函数名的形式调用函数,还允许您向不同类型的函数传递不同数量的参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function fnCall(fn, ...args)
{
  let func = (typeof fn =="string")?window[fn]:fn;
  if (typeof func =="function") func(...args)
  else console.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


所以,正如其他人所说,最好的选择是:

1
window['myfunction'](arguments)

正如JasonBunting所说,如果函数名包含一个对象,那么它将不起作用:

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

下面是我的函数版本,它将按名称执行所有函数(包括对象与否):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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');


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  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');

更多OOP解决方案…


关于杰森和亚历克斯的帖子的更多细节。我发现向上下文添加默认值很有帮助。只需将context = context == undefined? window:context;放在函数的开头。您可以将window更改为您首选的上下文,然后在默认上下文中每次调用它时,您就不需要传入相同的变量。


我的代码中有一个非常相似的东西。我有一个服务器生成的字符串,其中包含一个函数名,我需要将其作为第三方库的回调传递。所以我有一个代码,它接受字符串并返回一个指向函数的"指针",如果找不到,则返回空值。

我的解决方案非常类似于"JasonBunting的非常有用的函数"*,虽然它不自动执行,并且上下文总是在窗口上。但这很容易修改。

希望这对某人有帮助。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
 * 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;
}


最简单的方法是像has元素一样访问它

1
window.ClientSideValidations.forms.location_form

一样

1
window.ClientSideValidations.forms['location_form']

为了补充Jason Bunting的答案,如果你使用nodejs或其他东西(这在dom js中也适用),你可以使用this而不是window(记住:eval是邪恶的:

1
this['fun'+'ctionName']();

我忍不住要提到另一个技巧,如果有未知数量的参数也作为包含函数名的字符串的一部分传递,这将有所帮助。例如:

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

如果您的javascript运行在一个HTML页面上,那么您所需要的只是一个不可见的链接;您可以将一个字符串传递到onclick属性,并调用click方法。

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

或者在运行时创建元素。


还有一些非常有用的方法。

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

1
2
3
4
5
6
7
var arrayMaker = {  
    someProperty: 'some value here',  
    make: function (arg1, arg2) {  
        return [ this, arg1, arg2 ];  
    },
    execute: function_name
};

由于eval()是邪恶的,而new Function()不是实现这一点的最有效方法,因此这里有一个快速的js函数,它从字符串中的名称返回函数。

  • 适用于命名空间的函数
  • 如果字符串为空/未定义,则回滚到空函数
  • 如果找不到函数,则回退到空函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    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);

    }

用途:

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


您所要做的就是使用一个上下文或定义一个您的函数所在的新上下文。您不限于window["f"]();

下面是一个示例,说明如何对某些REST服务使用动态调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/*
Author: Hugo Reyes
@ www.teamsrunner.com

*/


    (function ( W, D) { // enclose it as 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.

如果您想从一个字符串生成整个函数,这是另一个答案。另外,请注意,您不限于单个名称空间,如果您的名称空间以my.name.space.for.functions.etc.etc.etc的形式存在,则您名称空间的最后一个分支包含my.name.space.for.functions.etc.etc["function"]();的功能。

希望它有帮助。H.


您也可以在eval("functionname as string")中调用javascript函数。如下:(eval是纯javascript函数)

1
2
3
4
5
6
7
8
function testfunc(){
    return"hello world";
}

$( document ).ready(function() {

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

工作示例:https://jsfiddle.net/suatatan/24ms0fna/4/


谢谢你的帮助。我正在我的项目中使用Jason Bunting的函数。

我将它扩展为与可选超时一起使用,因为设置超时的正常方法不起作用。见阿披实基诺的问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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 );


基本情况:

1
2
3
4
5
6
7
8
9
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

存在其他类型的函数是类和示例nils petersohn


这对我很有用:

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

我希望这能奏效。


1
2
3
4
5
6
7
8
9
10
11
use this

function executeFunctionByName(functionName, context /*, args */) {
      var args = [].slice.call(arguments).splice(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);
    }


不使用eval('function()'),您可以使用new Function(strName)创建一个新函数。下面的代码是用FF,Chrome,IE测试的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<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()');
  }

这里有几个executeByName函数可以很好地工作,除非名称包含方括号(我遇到的问题),因为我动态生成了名称。所以上面的函数会在如下名称上失败

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

作为补救措施,我也考虑到了这一点,也许有人会发现它是有用的:

从coffeescript生成:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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);
}

要获得更好的可读性,请检查coffeeesccript版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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)