ClojureScript-将任意JavaScript对象转换为Clojure脚本映射
我正在尝试将Javascript对象转换为Clojure。但是,我得到以下错误:ClojureScript-将任意JavaScript对象转换为Clojure脚本映射,clojure,clojurescript,Clojure,Clojurescript,我正在尝试将Javascript对象转换为Clojure。但是,我得到以下错误: (js/console.log (js->clj e)) ;; has no effect (pprint (js->clj e)) ;; No protocol method IWriter.-write defined for type object: [object Geoposition] 是的,此对象来自地理定位API。我想我必须扩展encodeclojure和IWriter,但我不知道
(js/console.log (js->clj e)) ;; has no effect
(pprint (js->clj e)) ;; No protocol method IWriter.-write defined for type object: [object Geoposition]
是的,此对象来自地理定位API。我想我必须扩展encodeclojure
和IWriter
,但我不知道如何扩展
例如,添加以下内容:
(extend-protocol IEncodeClojure
Coordinates
(-js->clj [x options]
(println "HERE " x options)))
加载我的代码时产生错误:
uncaughttypeerror:无法读取未定义的属性“prototype”
js->clj
仅适用于对象
,任何带有自定义构造函数的内容(请参见类型
)都将按原样返回
见:
我建议这样做:
(defn jsx->clj
[x]
(into {} (for [k (.keys js/Object x)] [k (aget x k)])))
更新有关正确的解决方案,请参见Aaron的答案,必须使用
goog.object
已接受的答案对javascript对象window.performance.timeing
不适用。这是因为Object.keys()
实际上并不返回performanceting
对象的道具
(.keys js/Object (.-timing (.-performance js/window))
; => #js[]
尽管事实上,performanceting
的道具确实适用于普通的JavaScript循环:
for (a in window.performance.timing) {
console.log(a);
}
// navigationStart
// unloadEventStart
// unloadEventEnd
// ...
下面是我将任意JavaScript对象转换为ClojureScript映射的方法。注意使用了两个简单的Google闭包函数
包装了goog.typeOf
,我们通常无法在ClojureScript中访问它。我用它来过滤掉那些功能性的道具typeOf
包装goog.object.getKeys
,建立一个数组结果,我们可以将其简化为一个映射for(obj中的prop){…}
(定义obj->clj
[obj]
(->)(fn[结果键]
(让[v(goog.object/get obj key)]
(如果(=“功能”(goog/typeOf v))
结果
(助理结果键v)
(reduce{}(.getKeys-goog/object-obj)))
解决方案(递归)
更新:此解决方案适用于嵌套贴图
(定义obj->clj
[obj]
(如果(goog.isObject obj)
(->)(fn[结果键]
(让[v(goog.object/get obj key)]
(如果(=“功能”(goog/typeOf v))
结果
(关联结果键(obj->clj v()(())))
(reduce{}(.getKeys-goog/object-obj)))
(obj)
两种不需要编写自定义转换函数的方法-它们都使用标准JavaScript函数来释放自定义原型,从而使clj->js
能够正常工作
使用JSON序列化
这种方法只是序列化为JSON并立即对其进行解析:
(js->clj (-> e js/JSON.stringify js/JSON.parse))
优势:
- 不需要任何辅助函数
- 适用于嵌套对象,带/不带原型
- 每个浏览器都支持
- 在代码库的关键部分,性能可能是一个问题
- 将剥离任何不可序列化的值,如函数
使用Object.assign() 这种方法基于,它通过将
e
中的所有属性复制到一个新的、简单的(没有定制原型)的#js{}
上来工作
(js->clj (js/Object.assign #js {} e))
优势:
- 不需要任何辅助函数
- 适用于平面对象,如果有另一个嵌套对象使用
,它将不会被e
转换clj->js
,最著名的是-IEObject.assign()
我还通过查看js->clj source添加了关键字设置选项你确定你有一个对象而不是
未定义的
?(js/console.log(undefined?e))
产生了什么?@TimPote它不是未定义的:使用Clojure音色,我得到了对象的名称。使用js/console.log,我在执行(js/console.log e)
和(js/console.log(js->clj e))
时得到了相同的js对象。与其他解决方案相比有什么优势吗?@nha I实际上使用了一种不同的方法(JSON序列化)。更新了我的答案,解释了这两种方法,以及每种方法的优缺点。Object.assign()
不适用于window.performance.timeing
aget
不应用于获取JS对象的值:aget
不应用于获取JS对象的值:@kamituel,感谢您指出这一点。我已经更新了我的答案,使用goog.object/get
。这太棒了!你将如何对这些键进行关键词化?要进行关键词化,似乎很容易将整个(if…
)包装成(clojure.walk/keywordize-keys(if…)
@RobertJBerger,对其进行优化,将第8行的键更改为(关键字键)
。
(defn obj->clj
([obj]
(obj->clj obj :keywordize-keys false))
([obj & opts]
(let [{:keys [keywordize-keys]} opts
keyfn (if keywordize-keys keyword str)]
(if (and (not-any? #(% obj) [inst? uuid?])
(goog.isObject obj))
(-> (fn [result k]
(let [v (goog.object/get obj k)]
(if (= "function" (goog/typeOf v))
result
(assoc result (keyfn k) (apply obj->clj v opts)))))
(reduce {} (.getKeys goog/object obj)))
obj))))