Как выполнить функцию JavaScript, имея её имя в виде строки
У меня есть имя функции в JavaScript в виде строки. Как мне преобразовать это имя в указатель на функцию, чтобы я мог вызвать её позже?
В зависимости от ситуации мне может понадобиться передавать различные аргументы в эту функцию.
Некоторые из функций могут быть в форме namespace.namespace.function(args[...])
.
Как решить эту задачу?
5 ответ(ов)
Да, вы можете использовать этот подход для выполнения JavaScript-кода динамически. Ваш пример создаёт новую функцию с помощью конструктора Function
, и вы можете вызывать её так же, как и любую другую функцию. Обратите внимание, что код, передаваемый в конструктор, должен быть корректным и защищённым, чтобы избежать потенциальных уязвимостей, связанных с выполнением произвольного кода.
Вот ваш код:
var codeToExecute = "My.Namespace.functionName()";
var tmpFunc = new Function(codeToExecute);
tmpFunc();
Такой способ также позволяет выполнять любой другой JavaScript-код, что может быть полезно в определённых сценариях. Не забудьте учесть безопасность при работе с динамическим выполнением кода в вашем приложении.
В ES6 можно получить доступ к методам класса по имени. Вот пример:
class X {
method1() {
console.log("1");
}
method2() {
this['method1'](); // Вызов метода method1 с использованием квадратных скобок
console.log("2");
}
}
let x = new X();
x['method2'](); // Вызов метода method2 с использованием квадратных скобок
Результат выполнения кода будет следующим:
1
2
Таким образом, вы можете использовать синтаксис с квадратными скобками для вызова методов по их именам в строковом формате.
Два момента:
- Избегайте использования
eval
, это крайне небезопасно и медленно. - Во-вторых, не имеет значения, где находится ваша функция, "глобальность" не имеет значения. Вызов
x.y.foo()
можно выполнить черезx.y['foo']()
илиx['y']['foo']()
, или даже черезwindow['x']['y']['foo']()
. Таким образом, вы можете цеплять вызовы до бесконечности.
В зависимости от контекста, в котором вы находитесь, вы можете использовать следующие варианты для вызова функции по имени:
this["funcname"]();
self["funcname"]();
window["funcname"]();
top["funcname"]();
globalThis["funcname"]();
Если вы работаете в Node.js, вы можете воспользоваться следующим:
global["funcname"]();
Каждый из этих подходов обращается к глобальному контексту в зависимости от среды выполнения, что позволяет вызывать функции, используя их имя как строку.
Вот мой вклад в отличные ответы Джейсона Бунтинга и Алекса Назарова, в котором я добавляю проверку ошибок, запрошенную 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 не является функцией
*/
Если у вас есть какие-либо вопросы или предложения по улучшению этой функции, не стесняйтесь задавать их!
Как перенаправить на другую веб-страницу?
Прокрутка к элементу с использованием jQuery
Как перемешать (сделать случайным) массив в JavaScript?
Где найти документацию по форматированию даты в JavaScript?
Как определить нажатие клавиши Esc?