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

Kie*_*ron 994 javascript

我在JavaScript中将函数的名称作为字符串.如何将其转换为函数指针,以便稍后调用?

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

一些功能可能采取的形式namespace.namespace.function(args[...]).

Jas*_*ing 1382

不要使用,eval除非你绝对,没有其他选择.

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

window["functionName"](arguments);
Run Code Online (Sandbox Code Playgroud)

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

window["My.Namespace.functionName"](arguments); // fail
Run Code Online (Sandbox Code Playgroud)

这是你怎么做的:

window["My"]["Namespace"]["functionName"](arguments); // succeeds
Run Code Online (Sandbox Code Playgroud)

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

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);
}
Run Code Online (Sandbox Code Playgroud)

你会这样称呼它:

executeFunctionByName("My.Namespace.functionName", window, arguments);
Run Code Online (Sandbox Code Playgroud)

注意,你可以传入你想要的任何上下文,所以这将与上面相同:

executeFunctionByName("Namespace.functionName", My, arguments);
Run Code Online (Sandbox Code Playgroud)

  • 从头开始 - 代码很清楚,知道的代码知道了.如果您像我一样,并且知道自己在做什么,那么如果您使用此代码,则可以自行进行此类更改.Stack Overflow用于教育他人,我认为我的代码更容易让新手理解.谢谢! (101认同)
  • 当然,我知道 - 但是我写这个函数的方式为那些阅读它的人提供了一些清晰度,可能并不能完全理解正在发生的事情.我写了这个函数,意识到阅读它的人可能需要一些帮助.我会提供一个替代,因为你问... (15认同)
  • 我觉得这里有问题.当你调用`My.Namespace.functionName()`时,`this`将引用`My.Namespace`对象.但是当你调用`executeFunctionByName("My.Namespace.functionName",window)`时,就没有办法让`this`引用相同的东西.也许它应该使用最后一个命名空间作为范围,或者如果没有命名空间则使用`window`.或者您可以允许用户将范围指定为参数. (5认同)
  • 你知道你不需要整个"func"构造吗?单独"context.apply"就可以了 (4认同)
  • 是否存在window ["funcName"]返回undefined的情况?这就是我现在遇到的问题.调用代码和函数在两个单独的js文件中定义.我尝试将它们添加到同一个文件中但没有区别. (3认同)
  • 在javascript 1.8中你可以做`functionName.split('.').reduce(function(obj,key){return obj [key];},window);` (3认同)
  • 只有将它放入全局范围时,`executeFunctionByName`才有效.如果你不这样做,你会得到`Uncaught TypeError:Illegal invocation`.我认为最后一行`return context [func] .apply(this,args);`应该是`return context [func] .apply(window,args);` (2认同)
  • @brentonstrine再试一次,不用onload包装javascript代码:http://jsfiddle.net/pgpLmdwn/ (2认同)

小智 98

只是觉得我发布了一个略有改动版的Jason Bunting非常有用的功能.

首先,我通过向slice()提供第二个参数来简化第一个语句.原始版本在IE以外的所有浏览器中都运行良好.

其次,我在return语句中用上下文替换了 ; 否则,当执行目标函数时,总是指向窗口.

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);
}
Run Code Online (Sandbox Code Playgroud)

  • 我提供了一个包括错误检查的答案。 (3认同)

Eli*_*ght 60

这个问题的答案告诉你如何做到这一点:Javascript相当于Python的本地人()?

基本上,你可以说

window["foo"](arg1, arg2);
Run Code Online (Sandbox Code Playgroud)

或许多其他人建议,你可以使用eval:

eval(fname)(arg1, arg2);
Run Code Online (Sandbox Code Playgroud)

虽然这是非常不安全的,除非你完全确定你正在评估什么.

  • 当其他所有方法都失败时,只使用eval作为最后的手段. (17认同)
  • 第一种形式是更可取的 (5认同)
  • 它是......但是它可以与这样的函数一起使用吗:xyz(args)? (2认同)

小智 50

你能不能这样做:

var codeToExecute = "My.Namespace.functionName()";
var tmpFunc = new Function(codeToExecute);
tmpFunc();
Run Code Online (Sandbox Code Playgroud)

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

  • 这与`eval("My.Namespace.functionName()")有什么不同?`? (11认同)
  • 甚至在使用函数传递参数时也能正常工作 (2认同)
  • @developerbmw,这里是答案http://stackoverflow.com/questions/4599857/are-eval-and-new-function-the-same-thing (2认同)
  • “不过,使用 Function() 构造函数并不是一个好主意(它和 eval() 一样糟糕),因为代码是作为字符串传递并求值的。” --Javascript 模式,第 4 章,第 58 页 (2认同)

Rub*_*rio 43

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

var customObject = {
  customFunction: function(param){...}
};
Run Code Online (Sandbox Code Playgroud)

然后你可以打电话:

customObject['customFunction'](param);
Run Code Online (Sandbox Code Playgroud)

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


nil*_*ohn 28

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

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

输出将是:

1
2
Run Code Online (Sandbox Code Playgroud)

  • 最好的 javascript **PURE** ...上帝..删除类不起作用,但没关系。谢谢! (2认同)
  • 这是我长期以来一直在寻找的东西。谢谢! (2认同)

ann*_*ata 23

两件事情:

  • 避免评估,它非常危险和缓慢

  • 其次,你的功能存在于哪里并不重要,"全球" - 无关紧要.x.y.foo()可通过启用x.y['foo']()x['y']['foo']()甚至window['x']['y']['foo']().你可以像这样无限期地链接.


Wol*_*ehn 15

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

如果函数存在于本地范围(也就是闭包)并且没有被其他一些本地对象引用,那么运气不好:你必须使用eval() AFAIK,看看 在javascript中动态调用本地函数

  • 杜德(或dudette),非常感谢您指出这一点!我以为我快要疯了。 (2认同)

小智 13

您只需要将字符串转换为指针window[<method name>].例:

var function_name = "string";
function_name = window[function_name];
Run Code Online (Sandbox Code Playgroud)

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


Mac*_*Mac 11

以下是我对Jason Bunting/Alex Nazarov的出色答案的贡献,其中包括Crashalot要求的错误检查.

鉴于此(人为的)序言:

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(){};
Run Code Online (Sandbox Code Playgroud)

然后是以下功能:

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 );
}
Run Code Online (Sandbox Code Playgroud)

将允许您通过存储在字符串中的名称调用javascript函数,无论是命名空间还是全局,带或不带参数(包括Array对象),提供有关遇到的任何错误的反馈(希望能够捕获它们).

示例输出显示了它的工作原理:

// 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
  */
Run Code Online (Sandbox Code Playgroud)

  • 嗯?SO 是一个问答/教学平台。我很乐意提供我能想到的所有例子,希望能传达出启示。对我来说,_这就是重点_。 (2认同)

Ahm*_*DAL 9

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

var myObject=new Object();
myObject["functionName"](arguments);
Run Code Online (Sandbox Code Playgroud)

例:

var now=new Date();
now["getFullYear"]()
Run Code Online (Sandbox Code Playgroud)


Zib*_*bri 9

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

this["funcname"]();
self["funcname"]();
window["funcname"]();
top["funcname"]();
globalThis["funcname"]();
Run Code Online (Sandbox Code Playgroud)

或者,在nodejs中

global["funcname"]()
Run Code Online (Sandbox Code Playgroud)


dyk*_*rad 8

小心!!!

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

原因1:某些代码混淆器会破坏您的代码,因为它们会更改函数名称,使字符串无效.

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


pou*_*yan 7

这是我的Es6方法,它使您可以通过名称作为字符串或函数名称来调用函数,还可以将不同数量的参数传递给不同类型的函数:

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
Run Code Online (Sandbox Code Playgroud)


abh*_*not 6

很惊讶没有提到setTimeout.

要运行不带参数的函数:

var functionWithoutArguments = function(){
    console.log("Executing functionWithoutArguments");
}
setTimeout("functionWithoutArguments()", 0);
Run Code Online (Sandbox Code Playgroud)

要使用参数运行函数:

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

要运行深度命名空间的函数:

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);
Run Code Online (Sandbox Code Playgroud)

  • 我喜欢这个答案,它似乎符合我的要求。 (2认同)

snn*_*snn 6

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");
Run Code Online (Sandbox Code Playgroud)

It will also work with imported functions:

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

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

const operations = {
  fun1,
  fun2
};

operations["fun1"]("Hello World");
operations["fun2"]("Hello World");
Run Code Online (Sandbox Code Playgroud)

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