我在JavaScript中将函数的名称作为字符串。如何将其转换为函数指针,以便稍后调用它?
根据具体情况,我可能还需要将各种参数传递给方法。
某些功能可能采用namespace.namespace.function(args[...])
。
答案 0 :(得分:1325)
不要使用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);
答案 1 :(得分:92)
我以为我会发布Jason Bunting's very helpful function稍微修改过的版本。
首先,我通过向 slice()提供第二个参数来简化第一个语句。原始版本在IE以外的所有浏览器中都运行良好。
其次,我已在return语句中用 context 替换 this ;否则,当执行目标函数时,此始终指向 window 。
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);
}
答案 2 :(得分:58)
这个问题的答案告诉你如何做到这一点:Javascript equivalent of Python's locals()?
基本上,你可以说
window["foo"](arg1, arg2);
或许多其他人建议,你可以使用eval:
eval(fname)(arg1, arg2);
虽然这是非常不安全的,除非你完全确定你正在评估什么。
答案 3 :(得分:49)
你能不能这样做:
var codeToExecute = "My.Namespace.functionName()";
var tmpFunc = new Function(codeToExecute);
tmpFunc();
您也可以使用此方法执行任何其他JavaScript。
答案 4 :(得分:38)
我认为这样做的一种优雅方式是在哈希对象中定义函数。然后,您可以使用字符串从哈希中引用这些函数。 e.g。
var customObject = {
customFunction: function(param){...}
};
然后你可以打电话:
customObject['customFunction'](param);
其中customFunction是与对象中定义的函数匹配的字符串。
答案 5 :(得分:24)
使用ES6,您可以按名称访问类方法:
class X {
method1(){
console.log("1");
}
method2(){
this['method1']();
console.log("2");
}
}
let x = new X();
x['method2']();
输出将是:
1
2
答案 6 :(得分:22)
两件事:
避免评估,这是非常危险和缓慢的
其次,你的功能存在于哪里并不重要,“全局” - 无关紧要。可以通过x.y.foo()
或x.y['foo']()
甚至x['y']['foo']()
启用window['x']['y']['foo']()
。你可以无限期地链接。
答案 7 :(得分:15)
所有答案都假设可以通过全局范围(aka窗口)访问这些函数。但是,OP没有做出这样的假设。
如果函数存在于本地范围(也称为闭包)并且未被其他本地对象引用,那么运气不好:您必须使用 eval() AFAIK,请参阅 dynamically call local function in javascript
答案 8 :(得分:13)
您只需要通过window[<method name>]
将字符串转换为指针。
例如:
var function_name = "string";
function_name = window[function_name];
现在你可以像指针一样使用它。
答案 9 :(得分:10)
以下是我对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 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
*/
答案 10 :(得分:9)
如果要使用window["functionName"]
调用对象的函数而不是全局函数。你可以这样做;
var myObject=new Object();
myObject["functionName"](arguments);
示例:
var now=new Date();
now["getFullYear"]()
答案 11 :(得分:8)
小心!!!
应该尽量避免在JavaScript中通过字符串调用函数,原因有两个:
原因1:某些代码混淆器会破坏您的代码,因为它们会更改函数名称,使字符串无效。
原因2:维护使用此方法的代码要困难得多,因为找到字符串调用的方法的用法要困难得多。
答案 12 :(得分:6)
这是我的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
答案 13 :(得分:5)
很惊讶没有提到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);
答案 14 :(得分:5)
根据您的所在位置,您还可以使用:
this["funcname"]();
self["funcname"]();
window["funcname"]();
top["funcname"]();
globalThis["funcname"]();
或者在nodejs中
global["funcname"]()
答案 15 :(得分:4)
我认为您不需要复杂的中间函数或eval或依赖于诸如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");
它还可以与导入的功能一起使用:
// 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");
由于它正在使用属性访问,它将在最小化或混淆中幸存下来,这与您在此处找到的一些答案相反。
答案 16 :(得分: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');
更多OOP解决方案......
答案 17 :(得分:3)
所以,和其他人一样,绝对是最好的选择:
console.logs
和Jason Bunting said一样,如果你的函数名包含一个对象,它将不起作用:
$ ionic run ios --device
所以这是我的一个函数版本,它将按名称执行所有函数(包括一个或不包含对象):
window['myfunction'](arguments)
答案 18 :(得分:2)
我的代码中有一个非常类似的东西。 我有一个服务器生成的字符串,其中包含一个函数名称,我需要将其作为第三方库的回调传递。所以我有一个代码,它接受字符串并返回一个&#34;指针&#34;到函数,如果找不到则为null。
我的解决方案非常类似于&#34; Jason Bunting's very helpful function&#34; *,虽然它不会自动执行,但上下文总是在窗口上。但这很容易修改。
希望这会对某人有所帮助。
/**
* 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;
}
答案 19 :(得分:2)
关于Jason和Alex的帖子的更多细节。我发现向上下文添加默认值很有帮助。只需将context = context == undefined? window:context;
放在函数的开头即可。您可以将window
更改为您喜欢的上下文,然后每次在默认上下文中调用此变量时都不需要传入相同的变量。
答案 20 :(得分:1)
还有一些非常有用的方法。
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
};
答案 21 :(得分:1)
我无法抗拒提及另一个技巧,如果你有一个未知数量的参数,它们也作为包含函数名称的字符串的一部分传递,那么会有所帮助。例如:
var annoyingstring = 'call_my_func(123, true, "blah")';
如果您的Javascript在HTML页面上运行,您只需要一个不可见的链接;您可以将字符串传递到onclick
属性,然后调用click
方法。
<a href="#" id="link_secret"><!-- invisible --></a>
$('#link_secret').attr('onclick', annoyingstring);
$('#link_secret').click();
或者在运行时创建<a>
元素。
答案 22 :(得分:1)
最简单的方法是像有元素一样访问它
window.ClientSideValidations.forms.location_form
与
相同window.ClientSideValidations.forms['location_form']
答案 23 :(得分:1)
要添加到Jason Bunting的答案,如果您正在使用nodejs或其他东西(这也适用于dom js),您可以使用this
代替window
(并记住:eval是邪恶:
this['fun'+'ctionName']();
答案 24 :(得分:0)
人们总是说eval
是危险和邪恶的,因为它可以运行任何任意代码。但是,如果您将eval与 whitelisting 方法一起使用,假设您知道所有可能需要提前运行的功能名称,那么eval不再是安全问题,因为输入为不再是任意。白名单是一种良好且频繁的安全模式。这是一个示例:
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')
答案 25 :(得分:0)
由于eval()
是邪恶的,并且new Function()
不是实现此目的的最有效方法,因此这里有一个快速的JS函数,它从字符串中的名称返回函数。
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); }
<强>用法:强>
convertStringtoFunction("level1.midLevel.myFunction")(arg1, arg2, ...);
答案 26 :(得分:0)
您所要做的就是使用上下文或定义您的函数所在的新上下文。
您不限于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 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"]();
希望它有所帮助。 小时。
答案 27 :(得分:0)
这是我最终为我的一个项目实施的一个有点强大且可重用的解决方案。
FunctionExecutor 构造函数
用法:
let executor = new FunctionExecutor();
executor.addFunction(two)
executor.addFunction(three)
executor.execute("one");
executor.execute("three");
显然在项目中,所有需要按名称调用的函数的添加都是通过循环完成的。
函数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);
}
}
示例用法:
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");
答案 28 :(得分:0)
您也可以在eval("functionname as string")
内调用javascript函数。如下所示:( eval是纯粹的javascript函数)
function testfunc(){
return "hello world";
}
$( document ).ready(function() {
$("div").html(eval("testfunc"));
});
答案 29 :(得分:0)
这里有几个executeByName
函数可以正常工作,除非名称包含方括号 - 我遇到了问题 - 因为我有动态生成的名称。因此上述函数将失败,如
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 = 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)
答案 30 :(得分:0)
感谢非常有用的答案。我在我的项目中使用Jason Bunting's function。
我将它扩展为使用可选的超时,因为设置超时的正常方法不起作用。见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 );
&#13;
答案 31 :(得分:0)
看起来很基本:
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
答案 32 :(得分:0)
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);
}
答案 33 :(得分:0)
不使用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>
答案 34 :(得分:0)
这对我有用:
var command = "Add";
var tempFunction = new Function("Arg1","Arg2", "window." + command + "(Arg1,Arg2)");
tempFunction(x,y);
我希望这有效。