Відповіді:
Не використовуйте, 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);
My.Namespace.functionName()
, this
будете посилатися на My.Namespace
об’єкт. Але коли ви телефонуєте executeFunctionByName("My.Namespace.functionName", window)
, немає ніякого способу змусити this
посилатися на те саме. Можливо, він повинен використовувати простір імен як область, або window
якщо немає просторів імен. Або ви можете дозволити користувачеві вказати область як аргумент.
Я просто подумав, що опублікую трохи змінену версію дуже корисної функції Джейсона Бантінга .
По-перше, я спростив перше твердження, надавши другий параметр slice () . Оригінальна версія працювала чудово у всіх браузерах, крім IE.
По- друге, я замінив це з контексту в зворотному заяві; в іншому випадку це завжди вказувало на вікно під час виконання цільової функції.
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, використовуючи цей метод.
eval("My.Namespace.functionName()");
?
var codeToExecute = "return My.Namespace.functionName()";
Я думаю, що елегантний спосіб зробити це, визначивши свої функції в хеш-об'єкті. Тоді ви можете мати посилання на ці функції з хеша, використовуючи рядок. напр
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
Object.create()
. const myObj = {method1 () {console.log ('1')}, method2 () {console.log ('2')}} myObj ['method1'] (); // 1 myObj ['method2'] (); // 2
Дві речі:
уникайте eval, це страшенно небезпечно і повільно
по-друге, не має значення, де існує ваша функція, "глобальна" -несутність не має значення. x.y.foo()
може бути включена через x.y['foo']()
або x['y']['foo']()
навіть window['x']['y']['foo']()
. Ви можете ланцюжок нескінченно, як це.
Усі відповіді передбачають, що до функцій можна отримати доступ через глобальну область (вікно). Однак ОП не зробила цього припущення.
Якщо функції живуть у локальному масштабі (він же закривається) і не посилаються на якийсь інший локальний об'єкт, невдача: Вам потрібно використовувати eval()
AFAIK, дивіться
динамічно викликати локальну функцію в javascript
Вам просто потрібно перетворити рядок у вказівник на window[<method name>]
. приклад:
var function_name = "string";
function_name = window[function_name];
і тепер ви можете використовувати його як вказівник.
Ось мій внесок у відмінні відповіді Джейсона Бантінга / Алекса Назарова, де я включаю перевірку помилок, яку вимагає Крашалот.
Враховуючи цю (надуману) преамбулу:
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
*/
if( typeof context[ functionName ] !== 'function' )
тому що контекст - вікно - визначено, є об'єктом і масивом, але вікно ['abcd'] не існує, як було визначено як проблема у прийнятому відповідь: window["My.Namespace.functionName"](arguments); // fail
БУДЬ ОБЕРЕЖНИЙ!!!
Слід уникати виклику функції по рядку в JavaScript з двох причин:
Причина 1: Деякі обфускатори коду будуть руйнувати ваш код, оскільки вони змінять назви функцій, зробивши рядок недійсним.
Причина 2: Набагато складніше підтримувати код, який використовує цю методологію, оскільки набагато складніше знаходити звичаї методів, викликаних рядком.
Ось мій підхід Es6, який дозволяє вам викликати свою функцію за назвою як рядок або ім'ям функції, а також дозволяє передавати різні числа аргументів різним типам функцій:
function fnCall(fn, ...args)
{
let func = (typeof fn =="string")?window[fn]:fn;
if (typeof func == "function") func(...args);
else throw new Error(`${fn} is Not a function!`);
}
function example1(arg1){console.log(arg1)}
function example2(arg1, arg2){console.log(arg1 + " and " + arg2)}
function example3(){console.log("No arguments!")}
fnCall("example1", "test_1");
fnCall("example2", "test_2", "test3");
fnCall(example3);
fnCall("example4"); // should raise an error in console
Здивовано, не побачивши жодної згадки про 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);
runMe
за допомогою кількох аргументів.
Отже, як говорили інші, безумовно, найкращий варіант:
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');
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');
Більше рішення OOP ...
Ще одна деталь щодо публікацій Джейсона та Алекса. Мені було корисно додати значення контексту за замовчуванням. Просто поставте 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;
}
Там теж якийсь дуже корисний спосіб.
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>
елемент під час виконання.
Ви можете зателефонувати в функцію 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/
Я не думаю, що вам потрібні складні проміжні функції або eval, або бути залежними від глобальних змінних, таких як window:
function fun1(arg) {
console.log(arg);
}
function fun2(arg) {
console.log(arg);
}
const operations = {
fun1,
fun2
};
let temp = "fun1";
try {
// You have to use square brackets property access
operations["fun1"]("Hello World");
operations["fun2"]("Hello World");
// You can use variables
operations[temp]("Hello World");
} catch (error) {
console.error(error);
}
Він також буде працювати з імпортованими функціями:
// mode.js
export function fun1(arg) {
console.log(arg);
}
export function fun2(arg) {
console.log(arg);
}
// index.js
import { fun1, fun2 } from "./mod";
const operations = {
fun1,
fun2
};
try {
operations["fun1"]("Hello World");
operations["fun2"]("Hello World");
} catch (error) {
console.error(error);
}
Без використання 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);
}
Вигляд основний:
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
Існує функція іншого типу класу, а приклад виглядає нульовим петерсоном
Дякую за дуже корисну відповідь. Я використовую функцію Джейсона Бантінга у своїх проектах.
Я розширив його, щоб використовувати його з додатковим тайм-аутом, тому що звичайний спосіб встановити час очікування не буде. Дивіться запитання 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 );