为什么我不能在js-> clj生成的序列中调用seq函数?

时间:2013-04-16 18:55:55

标签: clojure clojurescript

虽然我可以将一个简单的js对象变成一个类似于

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

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

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

该函数确实已应用,但结果对象不响应countfirst等序列函数,但我可以使用aget获取项目。我在chrome的控制台中得到的错误信息是;

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

为什么会这样? js->clj不应该使用所有对象吗?

我该如何解决这个问题?

谢谢!

2 个答案:

答案 0 :(得分:4)

js->clj传递instance?的后代时,isa?只会更改一个完全是JavaScript对象的内容(使用js\Object实现而不是js->clj,并且有充分的理由) 1}} aget返回相同的对象。 aset(和object[field-name])之所以有效,是因为它会编译为JavaScript上的ISeq语法。

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

答案 1 :(得分: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}))))

original code对此对象不起作用,因为它的类型必须完全是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] }`.