ClojureScript поддерживает оптимизацию :advanced
, при которой Google Closure Compiler будет переименовывать, вставлять или исключать (неиспользуемые) функции для реализации минимизации. Короче говоря, имя функции, которую вы хотите найти, в общем случае просто больше не будет существовать в :advanced
.
Из-за этого ClojureScript resolve
является средством времени компиляции (макрос, требующий буквально заключенного в кавычки символа).
Если вы используете :simple
или самодостаточный ClojureScript, вам доступны дополнительные параметры, поскольку необходимая поддержка сохраняется во время выполнения. Например, Планка имеет planck.core/resolve
, который ведет себя как Clojure resolve
. Подобный подход возможен в Lumo , и аналогичные средства могут быть созданы при использовании :simple
.
В общем, хотя, учитывая :advanced
, если вам нужно отобразить строки в набор функций, вам нужно каким-то образом организовать статическое отображение, построенное во время компиляции для поддержки этого (набор функций должен быть известен a priori , во время компиляции).
Если у вас есть пространство имен (имя которого статически известно во время компиляции), которое определяет функции, которые должны динамически вызываться через строки, вы можете рассмотреть возможность использования ns-publics
:
cljs.user=> (ns foo.core)
foo.core=> (defn square [x] (* x x))
#'foo.core/square
foo.core=> (in-ns 'cljs.user)
nil
cljs.user=> (when-some [fn-var ((ns-publics 'foo.core) (symbol "square"))]
(fn-var 3))
9
Это будет работать под :advanced
. Отображение, построенное с помощью ns-publics
, является статическим; построен во время компиляции. Если у вас есть несколько пространств имен, требующих такой обработки, вы можете merge
несколько вызовов ns-publics
, чтобы построить большую карту.
Преимущество этого подхода заключается в том, что используемый код довольно короткий и не требует значительного обслуживания. Недостатком является то, что он сбрасывает все открытые переменные пространства имен (foo.core
в этом примере) в ваш сгенерированный код (и сгенерированный код для vars несколько многословен). Другим недостатком является то, что вам необходимо статически знать пространство имен, задействованное во время компиляции.
Если вам нужно еще больше минимизировать размер сгенерированного кода, вы можете просто создать / поддерживать простую статическую карту из строки в значение функции, как в
(def fns {"square" foo.core/square})
и используйте его надлежащим образом, обновляя его по мере развития вашей кодовой базы.
Другой вариант - пометить функции, к которым вам нужно получить доступ, с помощью ^:export
meta, а затем вызвать эти функции с помощью взаимодействия JavaScript. Например, если вы определяете функцию таким образом
(defn ^:export square [x] (* x x))
тогда вы можете использовать strings / interop для поиска функции и вызова ее во время выполнения. Вот пример:
((goog.object/getValueByKeys js/window #js ["foo" "core" "square"]) 3)
Использование ^:export
и :advanced
покрыто здесь . Если вы знаете, что используете :simple
или меньше, вы можете просто использовать взаимодействие JavaScript для вызова интересующих функций, без необходимости использовать ^:export
.
Обратите внимание, что не существует общего решения, которое позволило бы вам искать функцию по имени во время выполнения в :advanced
, не добавляя какой-либо аспект этой функции в ваш код во время компиляции. (Фактически, если на функцию не ссылаются так, как статически компилятор Google Closure может видеть, реализация функции будет полностью исключена как мертвый код.) В приведенном выше примере ns-publics
захватывает все переменные для пространства имен при компиляции время, когда вы катите свою собственную карту поиска, устанавливает статический код для ссылки на значение функции и использует ^:export
статически, чтобы имя функции сохранялось во время выполнения.