Как выполнить функцию JavaScript, когда у меня есть его имя в виде строки

У меня есть имя функции в JavaScript как строка. Как преобразовать это в указатель на функцию, чтобы я мог позже называть его?

В зависимости от обстоятельств мне может потребоваться передать различные аргументы в метод.

Некоторые из функций могут иметь вид namespace.namespace.function(args[...]).

Ответы

Ответ 1

Не используйте eval если вы абсолютно, положительно не имеете другого выбора.

Как уже упоминалось, использование чего-то подобного было бы лучшим способом сделать это:

window["functionName"](arguments);

Это, однако, не будет работать с функцией namespace'd:

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

Ответ 2

Просто подумал, что я опубликую немного измененную версию Джейсона Бунтинга очень полезную функцию.

Во-первых, я упростил первый оператор, предоставив второй параметр 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);
}

Ответ 3

Ответ на этот другой вопрос показывает вам, как это сделать: Javascript эквивалент локальных пользователей Python()?

В принципе, вы можете сказать

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

или как многие другие предложили, вы можете просто использовать eval:

eval(fname)(arg1, arg2);

хотя это крайне опасно, если вы не уверены в том, что вы оцениваете.

Ответ 4

Не могли бы вы просто сделать это:

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

Вы также можете выполнить любой другой JavaScript, используя этот метод.

Ответ 5

Я думаю, что элегантный способ сделать это - определить ваши функции в хэш-объекте. Затем вы можете ссылаться на эти функции из хэша, используя строку. например.

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

Затем вы можете позвонить:

customObject['customFunction'](param);

Где customFunction будет строкой, соответствующей функции, определенной в вашем объекте.

Ответ 6

Две вещи:

  • избегать eval, это ужасно опасно и медленно

  • Во-вторых, не имеет значения, где ваша функция существует, "глобальная" -независимость не имеет значения. x.y.foo() можно включить с помощью x.y['foo']() или x['y']['foo']() или даже window['x']['y']['foo'](). Вы можете цеплять бесконечно так.

Ответ 7

С ES6 вы можете получить доступ к методам класса по имени:

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

вывод будет:

1
2

Ответ 8

Вам просто нужно преобразовать строку в указатель window[<method name>]. Пример:

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

и теперь вы можете использовать его как указатель.

Ответ 9

Все ответы предполагают, что к функциям можно получить доступ через глобальную область (aka window). Однако ОП не сделал этого предположения.

Если функции живут в локальной области (например, закрытии) и не ссылаются на какой-либо другой локальный объект, неудача: вы должны использовать eval() AFAIK, см. динамически вызывать локальную функцию в javascript

Ответ 10

Если вы хотите вызвать функцию объекта вместо глобальной функции с помощью window["functionName"]. Вы можете сделать это как:

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

Пример:

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

Ответ 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(){};

то следующая функция:

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

Ответ 12

ОСТОРОЖНО!

Следует избегать вызова функции по строке в JavaScript по двум причинам:

Причина 1: Некоторые обфускаторы кода разрушат ваш код, поскольку они изменят имена функций, делая строку недействительной.

Причина 2: Нам гораздо сложнее поддерживать код, который использует эту методологию, так как гораздо сложнее найти способы использования методов, называемых строкой.

Ответ 13

Удивлен, чтобы не упоминать 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

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

Более решение ООП...

Ответ 15

Еще одна деталь на сообщениях Джейсона и Алекса. Мне было полезно добавить значение по умолчанию в контекст. Просто поставьте context = context == undefined? window:context; в начале функции. Вы можете изменить window на любой предпочтительный контекст, а затем вам не нужно будет передавать одну и ту же переменную каждый раз, когда вы вызываете это в своем контексте по умолчанию.

Ответ 16

В моем коде есть очень похожее. У меня есть сгенерированная сервером строка, которая содержит имя функции, которое мне нужно передать в качестве обратного вызова для сторонней библиотеки. Поэтому у меня есть код, который берет строку и возвращает "указатель" на функцию, или null, если она не найдена.

Мое решение было очень похоже на "Джейсон Бантинг очень полезная функция *, хотя это не" t auto-execute, и контекст всегда находится в окне. Но это можно легко изменить.

Надеюсь, это кому-то поможет.

/**
 * 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;
}

Ответ 17

Итак, как и другие, определенно лучший вариант:

window['myfunction'](arguments)

И как сказал Джейсон Бантинг, он не будет работать, если имя вашей функции включает в себя объект:

window['myobject.myfunction'](arguments); // won't work
window['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');

Ответ 19

Я не могу удержаться от упоминания другого трюка, который помогает, если у вас есть неизвестное количество аргументов, которые также передаются как часть строки, содержащей имя функции. Например:

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> во время выполнения.

Ответ 20

Самый простой способ - получить к нему доступ, например, элемент

window.ClientSideValidations.forms.location_form

совпадает с

window.ClientSideValidations.forms['location_form']

Ответ 21

Это работает для меня:

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

Надеюсь, это сработает.

Ответ 22

Чтобы добавить к Jason Bunting ответ, если вы используете nodejs или что-то (и это работает в dom js тоже), вы можете использовать this вместо window (и помните: eval is evil

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

Ответ 23

Без использования 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>

Ответ 24

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

Ответ 25

Посмотрите основные:

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

Ответ 26

Спасибо за очень полезный ответ. Я использую функцию Джейсона Bunting в моих проектах.

Я продлил его, чтобы использовать его с дополнительным тайм-аутом, потому что нормальный способ установки тайм-аута не работает. См. abhishekisnot 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 );

Ответ 27

Здесь есть несколько функций 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)

Ответ 28

Вы можете вызвать функцию javascript внутри eval("functionname as string"). Как показано ниже: (eval - это чистая функция javascript)

function testfunc(){
    return "hello world";
}

$( document ).ready(function() {

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

Рабочий пример: https://jsfiddle.net/suatatan/24ms0fna/4/

Ответ 29

все, что вам нужно сделать, это использовать контекст или определить новый контекст, в котором вы работаете. вы не ограничены 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"]();

Надеюсь, это поможет. Н.

Ответ 30

Поскольку eval() является злым, а new Function() не является наиболее эффективным способом достижения этой цели, вот быстрая функция JS, которая возвращает функцию из ее имени в строке.

  • Работает для функций namespace'd
  • Возврат к нулевой функции в случае нулевой/неопределенной строки
  • Возврат к нулевой функции, если функция не найдена
    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, ...);