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

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

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

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

30 ответов


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

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

window["functionName"](arguments);

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

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

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

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

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

в основном, вы можете сказать

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

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

eval(fname)(arg1, arg2);

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


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

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

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


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

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

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

customObject['customFunction'](param);

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


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

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

выход будет:

1
2

две вещи:

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

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


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

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

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


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

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


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

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

пример:

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

вот мой вклад в отличные ответы Джейсона Бантинга / Алекса Назарова, где я включаю проверку ошибок, запрошенную 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 по имени, хранящемуся в строке, либо в пространстве имен или глобальном, с аргументами или без них( включая объекты массива), обеспечивая обратную связь по любым обнаруженным ошибкам (надеюсь, ловить их.)

пример вывода показывает, как это работает:

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

БУДЬТЕ ОСТОРОЖНЫ!!!

следует избегать вызова функции по строке в 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);

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

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


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


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

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

надеюсь это будет полезно кому-то.

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

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

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

там тоже очень полезный способ.

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

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

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


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

window.ClientSideValidations.forms.location_form

это то же самое, что

window.ClientSideValidations.forms['location_form']

чтобы добавить к ответу Джейсона Бантинга, если вы используете nodejs или что-то еще (и это работает в dom js тоже), вы можете использовать this вместо window (и помните: eval is зло:

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

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

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

Look basic:

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

существует функция другого типа класс и посмотрите пример Нилс petersohn


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

я расширил его, чтобы использовать его с дополнительным таймаутом, потому что обычный способ установить таймаут не будет работать. См.abhishekisnot вопрос

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 функции здесь, которые отлично работают, если имя не содержит квадратные скобки - проблема, с которой я столкнулся, - поскольку у меня динамически генерируемые имена. Таким образом, вышеуказанные функции будут терпеть неудачу на таких именах, как

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)

Вы можете вызвать функцию 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/


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

надеюсь, что это помогает. Х.


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

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

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


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

  • работает, что функции пространства имен
  • возврат к нулевой функции в случае null / undefined string
  • возврат к нулевой функции, если функция не найдена

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