Как выполнить функцию JavaScript, когда у меня есть ее имя в виде строки - PullRequest
945 голосов
/ 11 декабря 2008

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

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

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

Ответы [ 32 ]

1330 голосов
/ 11 декабря 2008

Не используйте 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);
92 голосов
/ 04 декабря 2010

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

Во-первых, я упростил первый оператор, указав второй параметр для slice () . Оригинальная версия работала нормально во всех браузерах, кроме IE.

Во-вторых, я заменил this на context в операторе возврата; в противном случае этот всегда указывал на окно , когда выполнялась целевая функция.

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);
}
58 голосов
/ 11 декабря 2008

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

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

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

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

eval(fname)(arg1, arg2);

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

49 голосов
/ 17 августа 2013

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

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

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

38 голосов
/ 02 июня 2015

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

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

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

customObject['customFunction'](param);

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

24 голосов
/ 08 июля 2015

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

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

вывод будет:

1
2
22 голосов
/ 11 декабря 2008

Две вещи:

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

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

15 голосов
/ 04 февраля 2014

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

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

13 голосов
/ 11 ноября 2011

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

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

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

10 голосов
/ 11 февраля 2017

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