我正在努力解决以下问题:
通常JS对象通过js->clj转换为ClojureScript。这适用于原型对象的对象。对于我正在使用的另一个:
(defn jsx->clj [o]
(reduce (fn [m v] (assoc m (keyword v) (aget o v))) {} (.keys js/Object o)))
我发现,作为幕后获取函数的"属性"不能通过这些操作进行转换。
有人对此有任何经验吗?
我不确定你说"幕后的 getter 函数不能通过这些操作转换"是什么意思。
其中一个问题是,当你像这样将JS对象转换为CLJS映射时,getter和setters将不会绑定到原始JS对象,因此它们将无法访问该对象的属性。
请考虑以下代码:
;; This is your function, no changes here.
(defn jsx->clj [o]
(reduce (fn [m v] (assoc m (keyword v) (aget o v))) {} (.keys js/Object o)))
;; This is an improved version that would handle JS functions that
;; are properties of 'o' in a specific way - by binding them to the
;; 'o' before assoc'ing to the result map.
(defn jsx->clj2 [o]
(reduce (fn [m v]
(let [val (aget o v)]
(if (= "function" (goog/typeOf val))
(assoc m (keyword v) (.bind val o))
(assoc m (keyword v) val))))
{} (.keys js/Object o)))
;; We create two JS objects, identical but distinct. Then we convert
;; both to CLJS, once using your function, and once using the improved
;; one.
;; Then we print results of accessing getter using different methods.
(let [construct-js (fn [] (js* "new (function() { var privProp = 5; this.pubProp = 9; this.getter = function(x) { return privProp + this.pubProp + x; }; })"))
js-1 (construct-js)
js-2 (construct-js)
clj-1 (jsx->clj js-1)
clj-2 (jsx->clj2 js-2)]
(.log js/console "CLJS objects: " (.getter js-1 10) ((:getter clj-1) 10) (.getter js-2 10) ((:getter clj-2) 10)))
这将打印:
CLJS objects: 24 NaN 24 24
这意味着以下代码:((:getter clj-1) 10)
失败,而((:getter clj-2) 10)
按预期工作。第二个有效,因为 .bind() 已被用于将函数与 JS 对象正确绑定。
这个问题,如果这确实是你失败的原因,相当于JS中有时会犯的以下错误:
var Constructor = function() {
var privProp = 5;
this.pubProp = 9;
this.getter = function(x) {
return privProp + this.pubProp + x;
};
}
var obj1 = new Constructor();
var obj2 = new Constructor();
var fn1 = obj1.getter; // Invalid, fn1 will not be bound to obj1.
var fn2 = obj2.getter.bind(obj2); // Note .bind here.
console.log(fn1(10), fn2(10));
这将打印类似的输出:
NaN 24
同样,尚未绑定fn1
obj1
返回无效输出。