3

虽然我可以将一个简单的 js 对象变成一个 clojure 对象,例如;

(-> "{a: 2, b: 3}" js* js->clj)

我显然无法goog.events.BrowserEvent在处理程序函数中对特定对象执行此操作,例如:

(defn handle-click [e]
  ...
  (-> e .-evt js->clj keys) ;; <-------------
  ...

该函数确实被应用了,但是生成的对象不响应像countor之类的序列函数first,尽管我可以使用aget. 我在 chrome 的控制台中收到的错误消息是;

Uncaught Error: No protocol
method ISeqable.-seq defined for type object: [object Object]

为什么会这样?不应该js->clj与所有对象一起工作吗?

我怎样才能解决这个问题?

谢谢!

4

2 回答 2

4

当您传递返回相同对象的后代时,js->clj唯一会更改完全是 JavaScript 对象的东西(它是使用instance?而不是实现的isa?,并且有充分的理由) 。(and ) 之所以有效,是因为它可以编译成JavaScript 的语法。js\Object js->cljagetasetobject[field-name]

您可以将ISeq协议(或任何其他协议)扩展到 ,goog.events.BrowserEvent并且所有可以使用的功能ISeq都可以使用goog.events.BrowserEvent. Chris Houser有一个演讲,他展示了如何将一堆协议扩展到 goog Map。我建议观看整个演讲,但与您的问题相关的部分从大约 14 分钟开始。

于 2013-04-21T16:34:36.647 回答
1

首先,我在 google 闭包中找到了获取对象键和值的函数:

 (defn goog-hash-map [object]
   (zipmap (goog.object/getKeys object) (goog.object/getValues object)))

然后,通过研究 cljs.core 的源代码,我意识到我所要做的就是用它扩展 IEncodeClojure 接口:

 (extend-protocol IEncodeClojure
   goog.events.BrowserEvent
   (-js->clj
    ([x {:keys [keywordize-keys] :as options}]
       (let [keyfn (if keywordize-keys keyword str)]
         (zipmap (map keyfn (gobj/getKeys x)) (gobj/getValues x))))
    ([x] (-js->cljs x {:keywordize-keys false}))))

原始代码不适用于此对象,因为它的类型必须完全是 Object。我试图将比较函数更改为实例?,即

(instance? x js/Object) (into {} (for [k (js-keys x)]
                                      [(keyfn k) (thisfn (aget x k))]))

但这也不起作用,挥舞着以下错误,这让我接受了以前的方法。

Uncaught TypeError: Expecting a function in instanceof check, 
but got function Object() { [native code] }`.
于 2013-04-17T01:20:31.140 回答