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

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

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

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

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1 124
0
567 800
34
Перейти к ответу Данный вопрос помечен как решенный

Ответы 34

Ответ на этот другой вопрос покажет вам, как это сделать: Эквивалент Python locals () в JavaScript?

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

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

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

eval(fname)(arg1, arg2);

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

первая форма намного предпочтительнее

annakata 11.12.2008 18:54

Используйте eval только в крайнем случае, когда ничего не помогает.

Jason Bunting 11.12.2008 18:58

Это ... но будет ли он работать с такими функциями: x.y.z (args)?

Kieron 11.12.2008 18:58

@keiron: да. см. мой ответ ниже

annakata 11.12.2008 19:02

Две вещи:

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

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

но вы не можете использовать window ['x.y.z'] () для вызова x.y.z ()

nickf 11.12.2008 19:11
Ответ принят как подходящий

Не используйте 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);

вы знаете, что вам не нужна вся конструкция "func"? Только "context.apply" нормально

annakata 11.12.2008 19:36

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

Jason Bunting 11.12.2008 19:50

Поцарапайте это - код достаточно ясен, и те, кто знает, знают. Если вы похожи на меня и знаете, что делаете, вы можете просто внести такие изменения самостоятельно, если вы использовали этот код. Stack Overflow предназначен для обучения других, и я думаю, что мой код легче понять новичкам. Спасибо хоть!

Jason Bunting 11.12.2008 20:21

Есть ли ситуация, когда window ["funcName"] вернет undefined? Это проблема, с которой я столкнулся в настоящий момент. Вызывающий код и функция определены в двух отдельных файлах js. Я попытался добавить их в тот же файл, но это не имело значения.

codemonkey 16.03.2010 14:19

executeFunctionByName работает, только если вы поместите его в глобальную область видимости. Если вы этого не сделаете, вы получите Uncaught TypeError: Illegal invocation. Думаю, последняя строчка return context[func].apply(this, args); должна быть return context[func].apply(window, args);

meze 25.03.2011 03:08

Я думаю, здесь проблема. Когда вы вызываете My.Namespace.functionName(), this будет ссылаться на объект My.Namespace. Но когда вы вызываете executeFunctionByName("My.Namespace.functionName", window), невозможно заставить this ссылаться на то же самое. Возможно, ему следует использовать последнее пространство имен в качестве области видимости или window, если пространств имен нет. Или вы можете разрешить пользователю указывать область в качестве аргумента.

JW. 07.07.2011 03:08

Отлично. Единственный вопрос - есть ли способ сделать функцию существующей (т.е. вызываемой) в executeFunctionByName ()?

Andy 03.09.2011 19:45

Я не могу передать несколько аргументов функции - что-то вроде executeFunctionByName("testFunction", window, new Array("false","<tr><td colspan='12'>Data loaded!</td></tr>")); не работает - может ли кто-нибудь помочь мне выяснить, что я делаю не так? - происходит то, что оба элемента в массиве передаются в первый аргумент функции testFunction.

Arvind Sridharan 20.09.2012 13:20

Можно ли передать ключевое слово new для создания экземпляров объектов по имени?

flynfish 14.02.2013 03:40
Uncaught TypeError: Object [object global] has no method 'x'
Brock Hensley 24.06.2013 07:12

Я пытался использовать эту версию, но она всегда выкидывала Illegal Invocation. Вариант @AlexNazarov (контекст как параметр для apply) работает как шарм.

DNax 04.10.2013 02:53

тогда переменная arguments глобальна?

Mirko 19.12.2013 20:41

Я не могу заставить это работать (я все время получаю не могу прочитать <x> of undefined. Может ли кто-нибудь, у кого он работает, опубликовать скрипку?

Sinaesthetic 21.04.2014 23:09

Не работает с IE8 и ниже. Используйте ответ Алекса Назарова, если вам нужна поддержка старых браузеров IE.

ajbeaven 26.05.2014 06:14

Это решение вообще не работает. Или я что-то упускаю?

brentonstrine 07.08.2014 02:37

@brentonstrine попробуйте еще раз, не упаковывая код javascript с помощью onload: jsfiddle.net/pgpLmdwn

maxgalbu 20.08.2014 19:03

В javascript 1.8 вы можете просто сделать functionName.split('.').reduce(function(obj, key){ return obj[key]; }, window);

backus 25.09.2014 09:29

Просто обратите внимание, если вы минимизируете свой JS. Возможно, вы захотите создать карту имен функций => функций, если хотите, чтобы это работало в минимизированном производственном коде.

Abdo 25.04.2015 15:32

К сожалению, этот ответ ошибочен, потому что он неправильно устанавливает контекст выполнения. Жалко, что все эти годы здесь не исправляли. Пожалуйста, используйте ответ @AlexNazarov ниже, он все правильно понимает.

hashchange 30.10.2015 12:16

Мне пришлось раскомментировать параметр args, а затем переопределить его значение в первой строке, чтобы TypeScript не волновался о том, что вызов не соответствует сигнатуре функции.

alan 24.05.2017 00:46

Имеет смысл, @alan - TypeScript несколько строг (вот почему я не люблю его использовать). ;)

Jason Bunting 24.05.2017 23:20

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

SJ00 02.06.2018 22:27

Кто-нибудь предлагает мне это. functionName = 'функция myFunction (аргумент) {alert (' Моя функция '+ аргумент);}'; window [myFunction] ('работает.'); можно ли выполнить метод, подобный приведенному выше?

Cegone 10.10.2019 12:12

Я обнаружил, что мне нужно определить функцию определенным образом для работы window ['myFn'] или MyNamespace ['myFn'}. Стрелочные функции не работали. myFn = function () {...} работал для global, а MyNamespace.myFn = function myFn () {...} работал для namespaced.

Little Brain 02.03.2020 18:52

@LittleBrain - этот вопрос и ответ довольно старые - в то время не существовало стрелочных функций ...

Jason Bunting 10.03.2020 19:34

Понятно, я просто хочу помочь всем, кто сталкивается с этим сейчас.

Little Brain 10.03.2020 21:51

Что, если функция, которую вы хотите вызвать, является импортом?

geoidesic 19.03.2020 12:55

Это решение не работает, если скрипт имеет тип = "модуль". Не спрашивайте меня, почему ... Пример: <script src = "test.js"></script> работает, а <script type = "module" src = "test.js"></script> не работает.

René K 20.05.2020 19:31

Я имею в виду свой комментарий выше: Причина, по которой он не работает: «В контексте модуля переменные не объявляются автоматически глобально». stackoverflow.com/a/49338383/5263954

René K 20.05.2020 19:41

Похоже, что еще никто не позаботился о переменной event. Я попытался протолкнуть его до конца args, и он работал, но он был поврежден - снова и снова возвращал 0 или одно и то же число (не могу объяснить, но попробовал с оригинальным eventListener, который работал правильно). Таким образом, метод call, вероятно, был бы лучшим способом исправить это, но как вы можете передать ему массив в качестве аргументов после события?

Luko Foks 13.03.2021 00:31

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

Во-первых, я упростил первый оператор, предоставив второй параметр ломтик(). Исходная версия нормально работала во всех браузерах, кроме 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);
}

Нет никакой проверки, существует ли "functionName" на самом деле?

Crashalot 02.11.2016 04:08

Я думаю, что ответ Мака недооценен. Я не эксперт, но он кажется хорошо продуманным и надежным.

Martin Hansen Lennox 10.03.2017 00:23

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

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

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

Это кажется более безопасным способом.

James Poulose 23.03.2016 19:20

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

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

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

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

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

Пример:

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

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

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

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

работает, когда даже аргументы передаются с функцией

adeel41 12.09.2014 22:13

А как насчет возврата функции?

Peter Denev 31.10.2014 12:08

Чем это отличается от eval("My.Namespace.functionName()");?

developerbmw 21.04.2015 03:36

@PeterDenev просто измените первую строку на var codeToExecute = "return My.Namespace.functionName()";

developerbmw 21.04.2015 03:40

Это сработало для меня, потому что я мог поддерживать аргументы в круглых скобках. Просто обратите внимание, что для этого подхода требуются круглые скобки после имени метода, в отличие от предыдущих ответов.

Savage 07.07.2016 13:07

@developerbmw, вот ответ stackoverflow.com/questions/4599857/…

Tejasvi Hegde 25.10.2016 10:53

У меня тоже сработало! Спасибо @Coley

Rupam Datta 27.02.2017 10:00

Спасибо. Работает в Firefox так же, как window ["functionStringName"], похоже, не работает в FF.

azsl1326 16.05.2018 18:54

«Однако использование конструктора Function () не является хорошей идеей (так же плохо, как eval ()), потому что код передается в виде строки и оценивается». - Паттерны JavaScript, глава 4, стр.58.

domih 18.11.2019 10:46

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

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

Чувак (или чувак), большое спасибо за то, что указали на это! На секунду я подумал, что схожу с ума.

Funktr0n 16.03.2014 07:13

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

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

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

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

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

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

Следует избегать вызова функции по строке в JavaScript по двум причинам:

Причина 1. Некоторые обфускаторы кода разрушат ваш код, поскольку они изменят имена функций, сделав строку недействительной.

Причина 2: намного сложнее поддерживать код, использующий эту методологию, так как гораздо сложнее определить случаи использования методов, вызываемых строкой.

Удивлен, что не увидел никакого упоминания о setTimeout.

Чтобы запустить функцию без аргументов:

var functionWithoutArguments = function(){
    console.info("Executing functionWithoutArguments");
}
setTimeout("functionWithoutArguments()", 0);

Чтобы запустить функцию с аргументами:

var functionWithArguments = function(arg1, arg2) {
    console.info("Executing functionWithArguments", arg1, arg2);
}
setTimeout("functionWithArguments(10, 20)");

Чтобы запустить функцию с глубоким пространством имен:

var _very = {
    _deeply: {
        _defined: {
            _function: function(num1, num2) {
                console.info("Execution _very _deeply _defined _function : ", num1, num2);
            }
        }
    }
}
setTimeout("_very._deeply._defined._function(40,50)", 0);

Это не дает ответа на вопрос. Чтобы критиковать или запрашивать разъяснения у автора, оставьте комментарий под его сообщением - вы всегда можете комментировать свои собственные сообщения, и как только у вас будет достаточно репутация, вы сможете комментировать любой пост.

AstroCB 18.10.2014 00:57

Пожалуйста, добавьте пример того, как бы вы позвонили runMe с несколькими аргументами.

lexicore 18.10.2014 01:01

Вы можете вызывать его как любую другую функцию. Просто это будет строка. var functionName = "addNumbers(10, 20)"; setTimeout(functionName,10);

abhishekisnot 18.10.2014 01:16

@abhishekisnot В следующий раз было бы лучше дать полный ответ, а не просто короткий фрагмент. Вопрос касается run.run.runMe, а не только runMe, а также аргументов. Пожалуйста, примите это как дружеский отзыв.

lexicore 18.10.2014 01:19

@AstroCB Этот делает дает элементарный ответ на вопрос. Поэтому здесь более уместно дать исправляющий отзыв, чем голосовать за закрытие.

lexicore 18.10.2014 01:21

@lexicore Я голосовал за удаление в очереди на рассмотрение, потому что четко не дает содержательного ответа на вопрос и сам по себе не представляет большой ценности.

AstroCB 18.10.2014 01:22

@lexicore благодарим за отзыв. Это была моя ошибка. Я предположил, что ответ был слишком тривиальным, чтобы привести пример. Отредактировал ответ.

abhishekisnot 18.10.2014 01:41

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

PeterM 15.11.2016 13:27

Мне нравится этот ответ, похоже, он соответствует моим требованиям.

Quintonn 18.09.2018 12:27

В моем коде есть очень похожая вещь. У меня есть сгенерированная сервером строка, содержащая имя функции, которое мне нужно передать в качестве обратного вызова для сторонней библиотеки. Итак, у меня есть код, который принимает строку и возвращает «указатель» на функцию или 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');

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

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

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

customObject['customFunction'](param);

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

ОБНОВИТЬ

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

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

const FunctionNames = Object.freeze({ 
  FirstFunction: "firstFunction", 
  SecondFunction: "secondFunction" 
});

...

var customObject = {
  [FunctionNames.FirstFunction]: function(param){...},
  [FunctionNames.SecondFunction]: function(param){...}
};

...

customObject[FunctionNames.FirstFunction](param);

@ibsenv, спасибо за ваш комментарий, который помог мне определить этот ответ как лучший. Я создал массив функциональных объектов и, в свою очередь, использовал его для создания массива deferred.promises. Я поместил пример кода ниже. (Я не хотел создавать новый ответ и ответ занимать Рубена.)

user216661 08.01.2016 19:55

function getMyData (arrayOfObjectsWithIds) {var functionArray = arrayOfObjectsWithIds.map (function (value) {return {myGetDataFunction: MyService.getMyData (value.id)};}) var promises = functionArray.map (function (getDataFunction) {var deferred q.defer (); getDataFunction.myGetDataFunction.success (функция (данные) {deferred.resolve (данные)}). error (function (error) {deferred.reject ();}); return deferred.promise;}); $ q.all (promises) .then (function (dataArray) {// делаем что-нибудь})};

user216661 08.01.2016 19:58

Это отлично работает, я добавляю только подчеркивание / lodash, чтобы проверить, является ли это функцией. А потом беги

elporfirio 21.09.2016 19:18

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

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

вывод будет:

1
2

Лучший javascript ЧИСТЫЙ ... Боже ... удалить класс не работает, но все в порядке. Спасибо!

KingRider 03.06.2016 16:51

Это то, что я искал очень давно. Спасибо!

PaladiN 22.05.2017 15:54

ES2015 здесь не при чем. Вы можете достичь той же цели, используя чистые объекты или делегирование прототипа через Object.create(). const myObj = {method1 () {console.info ('1')}, method2 () {console.info ('2')}} myObj ['method1'] (); // 1 myObj ['method2'] (); // 2

sminutoli 26.10.2018 05:54

Это золото !!! Я удивлен, что никогда раньше не думал об этом. Хороший!!!

thxmike 19.08.2019 04:39

Я также считаю, что это лучший способ достичь нашей цели.

Chris Jung 05.09.2019 18:50

Должен сказать, что это действительно намного безопаснее, чем пытаться сделать eval() или что-нибудь в этом роде. И тоже просто. Вы можете явно решить, что делать. Хорошее решение!

Pablo Alexis Domínguez Grau 10.11.2020 01:24

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

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

Креативное решение, но это не сработает для аргументов типа объекта или массива.

Dennis Heiden 11.10.2016 12:42

Это использует eval под капотом ... И действительно крутится вокруг да около, чтобы сделать это

Juan Mendes 26.05.2017 00:40

Без использования 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.info("error:"+err.message);
      }
  }

  function myFunction() {
    console.info('Executing myFunction()');
  }

</script>

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

window.ClientSideValidations.forms.location_form

такой же как

window.ClientSideValidations.forms['location_form']

Выглядите просто:

var namefunction = 'jspure'; // String

function jspure(msg1 = '', msg2 = '') { 
  console.info(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

Существуют другие функции типа класс и смотрите пример Нильс Петерсон.

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

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

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.info("Execution _very _deeply _defined _function : ", num1, num2);
            }
        }
    }
}

console.info('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/

Это прекрасно работает, и это так просто

Carlos E 16.10.2017 19:29

И тоже очень медленно.

Marco 01.09.2018 18:11

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

Учитывая эту (надуманную) преамбулу:

a = function( args ) {
    console.info( 'global func passed:' );
    for( var i = 0; i < arguments.length; i++ ) {
        console.info( '-> ' + arguments[ i ] );
    }
};
ns = {};
ns.a = function( args ) {
    console.info( 'namespace func passed:' );
    for( var i = 0; i < arguments.length; i++ ) {
        console.info( '-> ' + 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
  */

Не знаю ... это очень хорошее усилие, ясно. Но для меня звучит "слишком широко" ...

TechNyquist 29.03.2017 12:33

Хм? SO - это платформа вопросов / ответов / обучения. Я с радостью приведу все примеры, которые я могу придумать, чтобы передать освещение. Для меня в этом-то и дело.

Mac 08.12.2017 21:18

Если вы все равно оцениваете functionName, почему бы просто не использовать это?

data 27.09.2018 11:46

У меня это не работает. У меня есть функция с пространством имен a.b.c.d, где d - имя функции. вызов executeFunctionByName ("abcd", window) завершается неудачно в строке, которая проверяет if ( typeof context[ functionName ] !== 'function' ), потому что контекст - окно - определено, является объектом и массивом, но window ['abcd'] не существует, как было определено как проблема в принятый ответ: window["My.Namespace.functionName"](arguments); // fail

akousmata 09.05.2019 15:39
  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');

Еще одно решение ООП ...

все, что вам нужно сделать, это использовать контекст или определить новый контекст, в котором находятся ваши функции. вы не ограничены 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.info('Hi ' + name + ' inside function 1')
        }

        this.function2 = function (name) {

            console.info('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"]();

Надеюсь, поможет. ЧАС.

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

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

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

Вот мой подход 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.info(arg1)}
function example2(arg1, arg2){console.info(arg1 + "  and   " + arg2)}
function example3(){console.info("No arguments!")}

fnCall("example1", "test_1");
fnCall("example2", "test_2", "test3");
fnCall(example3);
fnCall("example4"); // should raise an error in console

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

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

или в nodejs

global["funcname"]()

Спасибо за этот ответ, возможно, function callObjectMethod(obj,meth){ return (_v) => { obj[meth](_v) } }. Для меня это полезно для вызова некоторого объектного метода с параметром, поступающим через обратный вызов от внешней службы. Надеюсь, это поможет кому-то другому.

Enrique René 18.12.2020 22:27

Люди продолжают говорить, что eval опасен и злобен, потому что может запускать любой произвольный код. Однако, если вы используете eval с подходом белый список, предполагая, что вы заранее знаете все возможные имена функций, которые, возможно, потребуется запустить, то eval больше не является проблемой безопасности, потому что вход больше не произвольный. Внесение в белый список - хороший и частый шаблон безопасности. Вот пример:

function runDynamicFn(fnName, ...args) {
  // can also be fed from a tightly controlled config
  const allowedFnNames = ['fn1', 'ns1.ns2.fn3', 'ns4.fn4'];

  return allowedFnNames.includes(fnName) ? eval(fnName)(...args) : undefined; 
}

// test function:
function fn1(a) { 
  console.info('fn1 called with', a)
}

runDynamicFn('alert("got you!")')
runDynamicFn('fn1', 'foo')

На мой взгляд, это все еще плохая реализация; лучше бы отобразить функции: let allowedFns = new Map(); allowedFns.set('fn1', fn1); allowedFns.set('ns1.ns2.fn3', ns1.ns2.fn3); .... Если использование eval безопасно, проблема, вероятно, может быть решена без eval :-P

Gershy 21.04.2020 18:32

Если вы знаете все имена функций, почему бы просто не создать массив с функциями?

Danial 30.08.2020 19:11

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

function fun1(arg) {
  console.info(arg);
}

function fun2(arg) {
  console.info(arg);
}

const operations = {
  fun1,
  fun2
};

operations["fun1"]("Hello World");
operations.fun2("Hello World");

// You can use intermediate variables, if you like
let temp = "fun1";
operations[temp]("Hello World");

Он также будет работать с импортированными функциями:

// mode.js
export function fun1(arg) {
  console.info(arg);
}

export function fun2(arg) {
  console.info(arg);
}
// index.js
import { fun1, fun2 } from "./mod";

const operations = {
  fun1,
  fun2
};

operations["fun1"]("Hello World");
operations["fun2"]("Hello World");

Поскольку он использует доступ к свойствам, он выдержит минимизацию или запутывание, вопреки некоторым ответам, которые вы найдете здесь.

Вот немного надежное и многоразовое решение, которое я реализовал для одного из своих проектов.

Функция-конструктор FunctionExecutor

Использование:

let executor = new FunctionExecutor();
executor.addFunction(two)
executor.addFunction(three)

executor.execute("one");
executor.execute("three");

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

Исполнитель функции:

function FunctionExecutor() {
  this.functions = {};

  this.addFunction = function (fn) {
    let fnName = fn.name;
    this.functions[fnName] = fn;
  }

  this.execute = function execute(fnName, ...args) {
    if (fnName in this.functions && typeof this.functions[fnName] === "function") {
      return this.functions[fnName](...args);
    }
    else {
      console.info("could not find " + fnName + " function");
    }
  }

  this.logFunctions = function () {
    console.info(this.functions);
  }
}

Пример использования:

function two() {
  console.info("two"); 
}

function three() {
  console.info("three");
}

let executor = new FunctionExecutor();
executor.addFunction(two)
executor.addFunction(three)

executor.execute("one");
executor.execute("three");

Другие вопросы по теме