Clojure/Clojurescript:解析的参数必须是带引号的符号

new*_*Dev 4 clojurescript

我正在尝试使用保存在变量中的字符串来调用如下函数:

(defn update-product-list [] "Test")

(defn handle-state-change [action]
  ((resolve (symbol action))))

(handle-state-change "update-product-list")
Run Code Online (Sandbox Code Playgroud)

但是,这给了我以下错误:断言失败:解决的参数必须是带引号的符号

我也尝试将上面的行更改为:

((resolve (quote (symbol action))))
Run Code Online (Sandbox Code Playgroud)

但这仍然会出错。我也尝试将其更改为:

((resolve 'action))
Run Code Online (Sandbox Code Playgroud)

但这给出了一个我不太明白的不同错误:js/action 被本地隐藏。我不想覆盖函数只是调用它。不知道我哪里出错了。我看过几个例子,但无法确定。

Mik*_*kes 7

ClojureScript 支持:advanced优化,其中 Google Closure Compiler 将重命名、内联或消除(未使用的)函数以实现缩小。简而言之,您要查找的函数的名称通常将不再存在于:advanced.

正因为如此,ClojureScriptresolve是一个编译时工具(一个需要文字引用符号的宏)。

如果您正在使用:simple或自托管 ClojureScript,则有更多选项可供您使用,因为所需的支持会持续到运行时。例如,普朗克有一个planck.core/resolve行为类似于 Clojure 的resolve。在Lumo 中可以使用类似的方法,如果使用:simple.

一般来说:advanced,如果您需要将字符串映射到一组函数,则需要以某种方式安排在编译时构造一个静态映射来支持这一点(在编译时必须先验地知道这组函数) .

如果您有一个命名空间(其名称在编译时静态已知),它定义了需要通过字符串动态调用的函数,则可以考虑使用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
Run Code Online (Sandbox Code Playgroud)

这将在:advanced. 构造的映射ns-publics是静态的;在编译时构建。如果您有多个需要此类处理的命名空间,您可以merge多次调用ns-publics以构建更大的映射。

这种方法的优点是涉及的代码非常短,几乎不需要维护。缺点是它将命名空间(foo.core在本例中)的所有公共变量转储到您生成的代码中(并且为 vars 生成的代码有些冗长)。另一个缺点是您需要静态地知道编译时涉及的命名空间。

如果您需要进一步最小化生成的代码大小,您可以构建/维护一个从字符串到函数值的简单静态映射,如

(def fns {"square" foo.core/square})
Run Code Online (Sandbox Code Playgroud)

并适当地使用它,随着代码库的发展保持最新状态。

另一种选择是使用^:export元标记您需要访问的函数,然后使用 JavaScript 互操作调用这些函数。例如,如果您以这种方式定义函数

 (defn ^:export square [x] (* x x))
Run Code Online (Sandbox Code Playgroud)

然后您可以使用字符串/互操作来查找该函数并在运行时调用它。下面是一个例子:

 ((goog.object/getValueByKeys js/window #js ["foo" "core" "square"]) 3)
Run Code Online (Sandbox Code Playgroud)

使用^:export:advanced覆盖在这里。如果你知道你正在使用:simple或更少,那么你可以简单地使用 JavaScript interop 来调用感兴趣的函数,而不必使用^:export.

请注意,没有通用的解决方案可以让您在运行时按名称查找函数,:advanced而无需在编译时以某种方式将该函数的某些方面放入您的代码中。(实际上,如果一个函数没有以 Google Closure Compiler 可以静态的方式引用,看,该函数实现将作为死代码完全消除。) 上面,ns-publics在编译时抓取命名空间的所有变量,滚动您自己的查找映射设置静态代码以引用函数值,并使用^:export静态排列使函数名称在运行时持久化。