如何执行JavaScript函数时,我有它的名字作为一个字符串

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

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

一些函数可能采用namespace.namespace.function(args[...])的形式。

660125 次浏览

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

基本上,你可以说

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

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

eval(fname)(arg1, arg2);

尽管这是非常不安全的,除非你绝对确定你在评估什么。

两件事:

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

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

不要使用eval,除非你别无选择。

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

window["functionName"](arguments);

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

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

你会这样做:

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

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

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);}

你会这样称呼它:

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

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

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

我只是想发布一个稍微修改过的版本Jason Bunting非常有用的功能

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

其次,我已经将这个替换为背景在返回语句中;否则,当目标函数被执行时,这个总是指向窗口

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);}

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

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

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

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

var arrayMaker = {someProperty: 'some value here',make: function (arg1, arg2) {return [ this, arg1, arg2 ];},execute: function_name};

关于Jason和Alex的文章还有一个细节。我发现向上下文添加默认值很有帮助。只需将context = context == undefined? window:context;放在函数的开头。您可以将window更改为您首选的上下文,这样每次在默认上下文中调用它时都不需要传入相同的变量。

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

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

示例:

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

你能不能不这样做:

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

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

所有的答案都假设函数可以通过全局作用域(窗口)访问。然而,OP没有做出这个假设。

如果函数位于本地范围(又名闭包)并且没有被其他本地对象引用,那么运气不好:您必须使用eval() AFAIK,请参阅在javascript中动态调用本地函数

这是为我工作:

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

我希望这能奏效。

为了补充Jason Bunting的回答,如果您使用nodejs或其他东西(这在dom js中也有效),您可以使用this而不是window(请记住:ava是邪恶

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

小心点!!!

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

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

原因2:维护使用这种方法论的代码要困难得多,因为定位字符串调用的方法的用法要困难得多。

很惊讶没有提到setTimeout。

运行不带参数的函数:

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

要使用参数运行函数:

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

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

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);

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

我的解决方案与“Jason Bunting非常有用的功能*非常相似,尽管它不会自动执行,并且上下文始终在窗口上。但这可以很容易地修改。

希望这对某人有帮助。

/*** 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, returnif (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 parsereturn null;}

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

window['myfunction'](arguments)

杰森·邦廷说一样,如果函数的名称包含一个对象,它将不起作用:

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

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

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');

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

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

然后你可以打电话:

customObject['customFunction'](param);

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

更新

似乎这个答案对许多编码人员很有帮助,所以这里有一个更新版本。

使用ES6,您还可以使用计算属性名称,这将允许您避免魔法字符串。

const FunctionNames = Object.freeze({FirstFunction: "firstFunction",SecondFunction: "secondFunction"});
...
var customObject = {[FunctionNames.FirstFunction]: function(param){...},[FunctionNames.SecondFunction]: function(param){...}};
...
customObject[FunctionNames.FirstFunction](param);

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

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

产出将是:

12

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

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

如果您的JavaScript在超文本标记语言页面上运行,您只需要一个不可见的链接;您可以将字符串传递给onclick属性,然后调用click方法。

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

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

或者在运行时创建<a>元素。

不使用eval('function()'),您可以使用new Function(strName)创建一个新函数。下面的代码使用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>

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

window.ClientSideValidations.forms.location_form

window.ClientSideValidations.forms['location_form']

看基本:

var namefunction = 'jspure'; // String
function jspure(msg1 = '', msg2 = '') {console.log(msg1+(msg2!=''?'/'+msg2:''));} // multiple argument
// Results ur testwindow[namefunction]('hello','hello again'); // something...eval[namefunction] = 'hello'; // use string or something, but its eval just one argument and not exist multiple

存在其他类型的函数是,看示例nils peter sohn

感谢您非常有用的回答。我在我的项目中使用Jason Bunting的功能

我将其扩展为与可选的超时一起使用,因为设置超时的正常方法不起作用。请参阅abhishekitnot的问题

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 );

这里有几个executeByName函数可以正常工作,除非name包含方括号-我遇到的问题-因为我有动态生成的名称。所以上面的函数会在像这样的名字上失败

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

作为一种补救措施,我也做了一个函数来考虑这一点,也许有人会发现它很有用:

从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);}

为了更好的易读性,也检查CoffeeScript版本:

executeByName = (name, context = window) ->args = Array.prototype.slice.call(arguments, 2)normalizedName = name.replace(/[\]'"]/g, '').replace(/\[/g, '.')ns = normalizedName.split "."func = contextfor n, i in nsfunc = func[n]
ns.pop()for n, i in nscontext = context[n];if typeof func != 'function'throw new TypeError 'Cannot execute function ' + namefunc.apply(context, args)

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

function testfunc(){return "hello world";}
$( document ).ready(function() {
$("div").html(eval("testfunc"));});

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

这是我对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(){};

然后是以下功能:

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函数,名称空间或全局,有或没有参数(包括Array对象),提供有关遇到的任何错误的反馈(希望能捕获它们)。

示例输出显示了它是如何工作的:

// calling a global function without parmsexecuteFunctionByName( '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 parmsexecuteFunctionByName( 'ns.a' );/* OUTPUT:namespace func passed:*/
// calling a namespaced function passing a string literalexecuteFunctionByName( '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 arrayexecuteFunctionByName( '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 literalexecuteFunctionByName( 'n_s_a' );/* OUTPUT:Uncaught n_s_a is not a function*/
// calling a non-existing function by string variableexecuteFunctionByName( n_s_a );/* OUTPUT:Uncaught Snowden is not a function*/
// calling an existing function with the wrong namespace referenceexecuteFunctionByName( 'a', {} );/* OUTPUT:Uncaught [object Object].a is not a function*/
// calling no functionexecuteFunctionByName();/* OUTPUT:Uncaught function name not specified*/
// calling by empty stringexecuteFunctionByName( '' );/* OUTPUT:Uncaught  is not a function*/
// calling an existing global function with a namespace referenceexecuteFunctionByName( 'noSuchAgency', ns );/* OUTPUT:Uncaught [object Object].noSuchAgency is not a function*/
  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的解决方案…

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

下面是我如何为某些REST服务使用动态调用的示例。

/*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 classfunction 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()是邪恶的,而new Function()不是实现这一点的最有效方法,这里有一个快速的JS函数,它从字符串中的名称返回函数。

  • 适用于命名空间的函数
  • 在null/未定义字符串的情况下回退到null函数
  • 如果未找到函数,则回退到null函数
function convertStringtoFunction(functionName){
var nullFunc = function(){}; // Fallback Null-Functionvar ret = window; // Top level namespace
// If null/undefined string, then return a Null-Functionif(functionName==null) return nullFunc;
// Convert string to function namefunctionName.split('.').forEach(function(key){ ret = ret[key]; });
// If function name is not available, then return a Null-Function else the actual functionreturn (ret==null ? nullFunc : ret);
}

用法:

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

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

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

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

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

或者,在nodejs

global["funcname"]()

人们一直说eval是危险和邪恶的,因为它可以运行任何任意代码。然而,如果你用白名单的方法使用ava,假设你知道所有可能需要提前运行的函数名,那么ava不再是一个安全问题,因为输入是不再任意。白名单是一种很好且频繁的安全模式。这是一个例子:

function runDynamicFn(fnName, ...args) {// can also be fed from a tightly controlled configconst 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')

我认为你不需要复杂的中间函数或ava,也不需要依赖于像windows这样的全局变量:

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 likelet temp = "fun1";operations[temp]("Hello World");

它还可以使用导入的函数:

// mode.jsexport function fun1(arg) {console.log(arg);}
export function fun2(arg) {console.log(arg);}
// index.jsimport { fun1, fun2 } from "./mod";
const operations = {fun1,fun2};
operations["fun1"]("Hello World");operations["fun2"]("Hello World");

由于它使用属性访问,因此它将在最小化或混淆中幸存下来,这与您在此处找到的一些答案相反。

这是一个有点健壮和可重用的解决方案,我最终为我的一个项目实现了这个解决方案。

函数执行器构造函数

用法:

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

显然,在项目中,需要按名称调用的所有函数的添加都是由循环完成的。

执行器的功能:

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);}}

示例用法:

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");

您可以将函数名称放在Array对象中,然后调用每个函数各自的数组键来执行它,演示

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 elementswitch(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;}}