Статьи

Как вызвать функцию JavaScript из строки без использования eval

eval это зло в JavaScript! Страница MDN eval гласит:

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

eval выполняет строку, содержащую код, например

 eval("var x = 'Hello from eval!';"); console.log(x); 

eval поднимает несколько вопросов:

  1. Безопасность: ваша строка может быть введена другими командами сторонними скриптами или пользовательским вводом.
  2. Отладка: сложно отладить ошибки — у вас нет номеров строк или явных точек сбоя.
  3. Оптимизация: интерпретатор JavaScript не обязательно может предварительно скомпилировать код, потому что он может измениться. Хотя интерпретаторы становятся все более эффективными, они почти наверняка будут работать медленнее, чем собственный код.

К сожалению, eval очень мощный, и менее опытным разработчикам легко злоупотребить командой.

Несмотря на предупреждения, eval прежнему работает — даже в строгом режиме — но вы обычно можете его избежать. В прошлом он в основном использовался для JSON.parse строк JSON, но теперь у нас есть более безопасный метод JSON.parse .

Однако, что если у нас есть имя функции в строке, например

 // function we want to run var fnstring = "runMe"; function runMe() { // do stuff } 

Как мы выполняем runMe() без использования eval ? Недавно я столкнулся с такой ситуацией при использовании API истории HTML5 ; метод pushState не позволит вам сохранить прямую ссылку на функцию, поэтому вам нужно определить ее имя в виде строки. Вы также можете столкнуться с аналогичными проблемами, используя Web Workers или любой другой API, где объекты сериализуются.

Самое простое и безопасное решение без выполнения — это ряд условий, например

 // function we want to run var fnstring = "runMe"; switch (fnstring) { case "functionX": functionX(); break; case "functionY": functionY(); break; case "functionZ": functionZ(); break; case "runMe": runMe(); break; } 

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

Лучшее решение — использовать объект window который ссылается на текущее окно и все элементы в нем. Мы можем проверить, доступен ли fnstring как объект в window и запустить его, если это функция, например

 // function we want to run var fnstring = "runMe"; // find object var fn = window[fnstring]; // is object a function? if (typeof fn === "function") fn(); 

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

Что если функция, которую мы хотим вызвать, имеет параметры — возможно, хранящиеся в массиве? Нет проблем; мы просто используем метод apply :

 // function name and parameters to pass var fnstring = "runMe"; var fnparams = [1, 2, 3]; // find object var fn = window[fnstring]; // is object a function? if (typeof fn === "function") fn.apply(null, fnparams); 

Так что это еще одна причина отказаться от использования eval . В качестве бонуса, это решение безопаснее, менее подвержено ошибкам, проще в отладке и, как правило, выполняется быстрее. Я надеюсь, что это помогает.