12

I was wondering if there was a way to get ClojureScript destructuring to work with JavaScript objects, such as:

(let [{:keys [a b]} #js{:a 10, :b 20}]
  (print a)  ;=> I get nil, but I'd want to get 10
  (print b)) ;=> I get nil, bu I'd want to get 20

Thank You

3 Answers 3

16

Associative destructuring is based on get, which can be mapped onto goog.object/get for JavaScript objects by extending them to ILookup:

(extend-type object
  ILookup
  (-lookup 
   ([o k] 
     (goog.object/get o (name k)))
   ([o k not-found] 
     (goog.object/get o (name k) not-found))))

Even though this results in destructuring working on JavaScript objects, it isn't advisable to extend object in this way. It would be preferable to decorate an object instance to achieve a similar effect.

Here is an example using reify:

(defn wrap [o]
  (reify
    ILookup
    (-lookup [_ k]
      (goog.object/get o (name k)))
    (-lookup [_ k not-found]
      (goog.object/get o (name k) not-found))))

With wrap as defined above:

(let [{:keys [a b]} (wrap #js{:a 10, :b 20})]
  (print a)
  (print b))

Functions in libraries that offer a capability like wrap above include:

Sign up to request clarification or add additional context in comments.

2 Comments

I'm now curious, what's the rationale for the advice against extending objects as such?
@DidierA. see Wikipedia Monkey patching can cause significant issues if they are malicious, poorly written, or insufficiently documented. They might break with upgrades if assumptions about the original code change, require careful conditional application, and can conflict with one another if multiple patches target the same method. This discrepancy between the modified behavior and the original source code can confuse developers, and patches can even include malicious code intended to harm the main program or interfere with other patches.
4

Matt Huebert's js-interop library provides JavaScript object destructuring out of the box with its version of let. All you have to do is add ^:js in front of the binding form:

(ns foo.bar
  (:require [applied-science.js-interop :as j]))

(j/let [^:js {:keys [a b]} #js{:a 10, :b 20}]
  [a b])
;; => [10 20]

This applies recursively (there's an escape hatch, ^:clj) and works in j/fn and j/defn as well. These functions act like normal Clojure if the ^:js is left out.

Comments

3

I haven't found a direct way to destructure JS object. You can convert JavaScript object to a ClojureScript datastructure and then destructure:

(let [{:keys [a b]} (js->clj #js {:a 10 :b 20}
                             :keywordize-keys true)]
  (print a)
  (print b))

If you don't use :keywordize-keys option in js->clj you need to use strs instead of keys in the destructuring

(let [{:strs [a b]} (js->clj #js {:a 10 :b 20})]
  (print a)
  (print b))

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.