Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
ClojureScript-将任意JavaScript对象转换为Clojure脚本映射_Clojure_Clojurescript - Fatal编程技术网

ClojureScript-将任意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,但我不知道

我正在尝试将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
,但我不知道如何扩展

例如,添加以下内容:

(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
    包装了
    typeOf
    ,我们通常无法在ClojureScript中访问它。我用它来过滤掉那些功能性的道具
  • 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
    转换
  • Object.assign()
    ,最著名的是-IE
上面原始版本的一个小问题是JS将#inst和#uuid视为对象。这些似乎是clojure中唯一标记的文本


我还通过查看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))))