我正在尝试使用保存在变量中的字符串来调用如下函数:
(defn update-product-list [] "Test")
(defn handle-state-change [action]
((resolve (symbol action))))
(handle-state-change "update-product-list")
但是,这给了我以下错误:声明失败:要解决的参数必须是带引号的符号
我也尝试将以上行更改为:
((resolve (quote (symbol action))))
但这仍然会产生错误。我还尝试将其更改为:
((resolve 'action))
但是这给出了一个我不太了解的错误:js / action被本地遮蔽。我不想重写功能,只是调用它。不知道我要去哪里错了。我看了几个例子,但看不出来。
答案 0 :(得分:2)
ClojureScript支持:advanced
优化,其中Google Closure Compiler将重命名,内联或消除(未使用)函数以实现缩小。简而言之,通常,您要查找的函数的名称将不再存在于:advanced
下。
因此,ClojureScript的resolve
是编译时工具(一个需要用文字引号引起来的宏)。
如果您使用:simple
或自托管的ClojureScript,则可以使用更多选项,因为所需的支持将持续到运行时。例如,Planck具有一个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
这将在:advanced
下运行。 ns-publics
构造的映射是静态的;在编译时构建。如果您有多个需要这种处理的名称空间,则可以merge
多次调用ns-publics
来构建更大的地图。
此方法的优点是所涉及的代码非常短,并且几乎不需要维护。缺点是它将名称空间的所有公共var(在此示例中为foo.core
)转储到生成的代码中(并且为vars生成的代码有些冗长)。另一个缺点是您需要在编译时静态地知道所涉及的名称空间。
如果您需要进一步最小化生成的代码大小,则可以像在
中那样构建/维护一个从字符串到函数值的简单静态映射。(def fns {"square" foo.core/square})
并适当地使用它,以使其随着代码库的发展而保持最新。
另一种选择是标记您需要使用^:export
元访问的功能,然后使用JavaScript interop调用这些功能。例如,如果您以这种方式定义函数
(defn ^:export square [x] (* x x))
然后,您可以使用字符串/互操作查找函数并在运行时调用它。这是一个示例:
((goog.object/getValueByKeys js/window #js ["foo" "core" "square"]) 3)
here涵盖了^:export
和:advanced
的使用。如果您知道使用的是:simple
或更少的代码,则可以简单地使用JavaScript互操作来调用感兴趣的函数,而无需使用^:export
。
请注意,没有通用的解决方案可以让您在运行时在:advanced
下按名称查找函数,而又不希望在编译时将该函数的某些方面放入代码中。 (实际上,如果未以Google Closure Compiler可以静态方式引用的函数,则该函数的实现将被完全删除为无效代码。)在上面,ns-publics
捕获名称空间的所有var在编译时,滚动您自己的查找映射会设置静态代码以引用该函数值,并使用^:export
进行静态安排以使该函数的名称持久到运行时。
答案 1 :(得分:0)
您需要像这样使用它:
((resolve 'inc) 5)) => 6
或者,解构一下:
(let [the-fn (resolve 'inc)]
(the-fn 7))
=> 8
如果函数名称为字符串,请使用symbol
函数从字符串=>符号(from clojuredocs.org)进行转换:
user=> ((-> "first" symbol resolve) [1 2 3])
1
而且,永远不要忘记the Clojure CheatSheet!