简体   繁体   中英

Convert JavaScript Object to ClojureScript: Getter and Setter Properites

I'm struggling with the following issue:

Usually JS objects are converted via js->clj to ClojureScript. This works for objects of the prototype Object. For the other's I'm using:

(defn jsx->clj [o]
  (reduce (fn [m v] (assoc m (keyword v) (aget o v)))  {} (.keys js/Object o)))

I discovered, that "properties", which are getter functions behind the scenes cannot be transformed by these operations.

Does anybody have any experiences with this?

I'm not sure what you mean when you say "getter functions behind the scenes cannot be transformed by these operations".

One of the issues is that when you convert JS object into CLJS map like that, getters and setters will not be bound to the original JS object, thus they will not have access to the properties of this object.

Consider the following code:

;; 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)))

This prints:

CLJS objects:  24 NaN 24 24

This means that the following code: ((:getter clj-1) 10) failed, while ((:getter clj-2) 10) worked as expected. The second one worked, because the .bind() has been used to tie function with the JS object properly.

This issue, if that's indeed what has failed for you, is the equivalent of the following mistake sometimes done in 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));

That prints similar output:

NaN 24

Again, fn1 that has not been bound to obj1 returned invalid output.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM