12

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

20

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

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

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

Как решить эту задачу?

5 ответ(ов)

0

Да, вы можете использовать этот подход для выполнения JavaScript-кода динамически. Ваш пример создаёт новую функцию с помощью конструктора Function, и вы можете вызывать её так же, как и любую другую функцию. Обратите внимание, что код, передаваемый в конструктор, должен быть корректным и защищённым, чтобы избежать потенциальных уязвимостей, связанных с выполнением произвольного кода.

Вот ваш код:

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

Такой способ также позволяет выполнять любой другой JavaScript-код, что может быть полезно в определённых сценариях. Не забудьте учесть безопасность при работе с динамическим выполнением кода в вашем приложении.

0

В ES6 можно получить доступ к методам класса по имени. Вот пример:

class X {
  method1() {
    console.log("1");
  }
  
  method2() {
    this['method1'](); // Вызов метода method1 с использованием квадратных скобок
    console.log("2");
  }
}

let x = new X();
x['method2'](); // Вызов метода method2 с использованием квадратных скобок

Результат выполнения кода будет следующим:

1
2

Таким образом, вы можете использовать синтаксис с квадратными скобками для вызова методов по их именам в строковом формате.

0

Два момента:

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

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

this["funcname"]();
self["funcname"]();
window["funcname"]();
top["funcname"]();
globalThis["funcname"]();

Если вы работаете в Node.js, вы можете воспользоваться следующим:

global["funcname"]();

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

0

Вот мой вклад в отличные ответы Джейсона Бунтинга и Алекса Назарова, в котором я добавляю проверку ошибок, запрошенную Crashalot.

Рассмотрим следующий (придуманный) преамбулу:

a = function( args ) {
    console.log( 'глобальная функция получена:' );
    for( var i = 0; i < arguments.length; i++ ) {
        console.log( '-> ' + arguments[ i ] );
    }
};
ns = {};
ns.a = function( args ) {
    console.log( 'функция из пространства имен получена:' );
    for( var i = 0; i < arguments.length; i++ ) {
        console.log( '-> ' + arguments[ i ] ); 
    }
};
name = 'nsa';
n_s_a = [ 'Сноуден' ];
noSuchAgency = function(){};

Следующая функция:

function executeFunctionByName( functionName, context /*, args */ ) {
    var args, namespaces, func;

    if( typeof functionName === 'undefined' ) { throw 'имя функции не указано'; }

    if( typeof eval( functionName ) !== 'function' ) { throw functionName + ' не является функцией'; }

    if( typeof context !== 'undefined' ) { 
        if( typeof context === 'object' && context instanceof Array === false ) { 
            if( typeof context[ functionName ] !== 'function' ) {
                throw context + '.' + functionName + ' не является функцией';
            }
            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 функцию по имени, хранящемуся в строке, как в глобальной, так и в именованной области, с аргументами или без них (включая массивы), обеспечивая обратную связь о любых встреченных ошибках (в идеале, ловя их).

Пример вывода демонстрирует, как это работает:

// вызов глобальной функции без параметров
executeFunctionByName( 'a' );
  /* ВЫВОД:
  глобальная функция получена:
  */

// вызов глобальной функции с передачей числа (с неявным контекстом window)
executeFunctionByName( 'a', 123 );
  /* ВЫВОД:
  глобальная функция получена:
  -> 123
  */

// вызов функции из пространства имен без параметров
executeFunctionByName( 'ns.a' );
  /* ВЫВОД:
  функция из пространства имен получена:
  */

// вызов функции из пространства имен с передачей строкового литерала
executeFunctionByName( 'ns.a', 'Нет такой службы!' );
  /* ВЫВОД:
  функция из пространства имен получена:
  -> Нет такой службы!
  */

// вызов функции из пространства имен, с явным контекстом в отдельном аргументе, с передачей строкового литерала и массива
executeFunctionByName( 'a', ns, 'Нет такой службы!', [ 007, 'это человек' ] );
  /* ВЫВОД:
  функция из пространства имен получена:
  -> Нет такой службы!
  -> 7,это человек
  */

// вызов глобальной функции с передачей строковой переменной (с неявным контекстом window)
executeFunctionByName( 'a', name );
  /* ВЫВОД:
  глобальная функция получена:
  -> nsa
  */

// вызов несуществующей функции через строковой литерал
executeFunctionByName( 'n_s_a' );
  /* ВЫВОД:
  Uncaught n_s_a не является функцией
  */

// вызов несуществующей функции по строковой переменной
executeFunctionByName( n_s_a );
  /* ВЫВОД:
  Uncaught Сноуден не является функцией
  */

// вызов существующей функции с неправильной ссылкой на пространство имен
executeFunctionByName( 'a', {} );
  /* ВЫВОД:
  Uncaught [object Object].a не является функцией
  */

// вызов без указания функции
executeFunctionByName();
  /* ВЫВОД:
  Uncaught имя функции не указано
  */

// вызов по пустой строке
executeFunctionByName( '' );
  /* ВЫВОД:
  Uncaught  не является функцией
  */

// вызов существующей глобальной функции с ссылкой на пространство имен
executeFunctionByName( 'noSuchAgency', ns );
  /* ВЫВОД:
  Uncaught [object Object].noSuchAgency не является функцией
  */

Если у вас есть какие-либо вопросы или предложения по улучшению этой функции, не стесняйтесь задавать их!

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь