如何在我只知道函数名的情况下执行JavaScript函数

1273

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

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

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

37个回答

2

以下是我最终在一个项目中实现的一个更加健壮和可重用的解决方案。

一个FunctionExecutor构造函数

使用方法:

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

2

关于Jason和Alex的帖子,还有一个细节。我发现在上下文中添加默认值是很有帮助的。只需在函数开头添加context = context == undefined? window:context;即可。您可以将window更改为您喜欢的任何上下文,并且这样您每次在默认上下文中调用它时都不需要传递相同的变量。


2

除了Jason Bunting的回答外,如果您正在使用nodejs或其他类似技术(在dom js中也适用),您可以使用this代替window(请记住:eval是邪恶的):

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

1
我不能抵制提及另一个技巧,如果您有许多未知数量的参数也作为包含函数名称的字符串一起传递,这将会很有帮助。例如:
var annoyingstring = 'call_my_func(123, true, "blah")';
如果您的Javascript运行在HTML页面上,您只需要一个不可见的链接;您可以将一个字符串传入onclick属性,然后调用click方法。
$('#link_secret').attr('onclick', annoyingstring);
$('#link_secret').click();

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

创意解决方案,但这对于对象或数组类型的参数不起作用。 - Dennis Heiden
1
这是在幕后使用 eval... 而且非常绕弯子才能做到 - Ruan Mendes

1

1
最简单的方法是像访问元素一样访问它。
window.ClientSideValidations.forms.location_form

等同于

window.ClientSideValidations.forms['location_form']

1
人们一直说eval很危险和邪恶,因为它可以运行任意代码。然而,如果你使用白名单方式来使用eval,并假设你提前知道可能需要运行的所有函数名称,则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')


在我看来,这仍然是一个较差的实现;更好的方法是映射函数:let allowedFns = new Map(); allowedFns.set('fn1', fn1); allowedFns.set('ns1.ns2.fn3', ns1.ns2.fn3); ...。如果使用 eval 是安全的,那么问题可能可以在不使用 eval 的情况下解决 :-P - Gershom Maes
如果您知道所有函数名称,为什么不创建一个包含这些函数的数组呢? - Danial

1

我喜欢简洁的解决方案,所以想出了这种更符合ES6的方式:

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

0

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

这里有一个我如何使用一些动态调用来处理 REST 服务的例子。

/* 
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.

如果你想从一个字符串生成整个函数,那就是另一个答案。 同时请注意,你不仅限于一个命名空间,如果你的命名空间存在于my.name.space.for.functions.etc.etc.etc,那么你的命名空间的最后一个分支包含了这个函数my.name.space.for.functions.etc.etc["function"]();

希望对你有所帮助。 H.


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

还有另一种类型的函数是,例如尼尔斯·彼得索恩


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接