January 3, 2020
By: anna

goog.object/getValueByKeys和aget更简便的取出#js的值

  1. aget的语法
  2. getValueByKeys的语法
  3. 参考资料

在clojure中我们经常会取到js类型的返回值,根据以往的经验,我们一般会选择用js->clj进行转换,然后get-in我们想要的值。我以为这个方法是万能的,但事实上并不是,下面让我们看看哪几种情况可以使用转化方法:

class myObject  {
  constructor(name) {
    this.name = name;
  }
}
window.myObj = new myObject("hello");

window.myMap = {"key": "value"};

window.myArray = ["Saab", "Volvo", "BMW"];

js map可以用js->clj进行转换

js/window.myMap
(js->clj js/window.myMap :keywordize-keys true)

js Array可以用js->clj进行转化

js/window.myArray
(js->clj  js/window.myArray :keywordize-keys true)

这些都是很常见的,但是还有些类型转化方法并不支持:

js object, NO!!!! js->clj不好用了

js/window.myObj
(js->clj js/window.myObj :keywordize-keys true)

最近就是遇到了这个难题。我想写一个滑动删除的功能,用到了on-touch-start事件,我需要获取的是里面的clientX。如果是用javascript的话,我们这么取值

event.touches[0].clientX

但是在clojure中,我们只能获取到这一步:

(js->clj (.-touches e) :keywordize-keys true)

通常我们想进一步获取里面的clientX,会这样写:

(get-in (js->clj (.-touches e) :keywordize-keys true) [0 :clientX])

但是失败了,返回的null,尝试了很过方法,都取不到值,最后求助了大神,进而发现有一种方法可以更快个安全的取到我们想要的结果。

aget或者goog.object/getValueByKeys可以从object中拿到值

(aget js/window.myObj "name")
(goog.object/getValueByKeys js/window.myObj
    #js ["name"])

那么我们再来看一下用这两种方法怎么从on-touch-start中取出clientX:

aget取值:

(-> e
    (.. -touches)
    (aget 0)
    (.. -clientX))

goog.object/getValueByKeys取值:

(goog.object/getValueByKeys e 
    #js ["touches" 0 "clientX"])

我再把两种语法详细补充一下:

aget的语法

定义一个obj

{foo: {entries: [{prop: "alpha"},
             {prop: "beta"}]}}


(aget obj "foo" "entries" 1 "prop")

getValueByKeys的语法

(:require [goog.object :as gobj])

先来看语法

(goog.object/getValueByKeys obj 
    #js ["foo" "entries" 1 "prop"])

google.object/getValueByKeys是作为get-in的JavaScript模拟。

我们在进行js->clj这种转化的时候是需要时间的,阻塞我们代码的运行。

不过呢,goog.object要快个几百倍

注意下面的性能测试, 分别执行100万次.

(simple-benchmark [obj #js {:foo #js {:entries #js [#js {:prop "alpha"} #js {:prop "beta"}]}}]
  (goog.object/getValueByKeys obj
    #js ["foo" "entries" 1 "prop"]) 1000000)
(simple-benchmark [obj #js {:foo #js {:entries #js [#js {:prop "alpha"} #js {:prop "beta"}]}}]
  (let [m (js->clj obj :keywordize-keys true)]
    (get-in m [:foo :entries 1 :prop])) 1000000)

看起来,把js数据转成clojure的结构非常不合算呀. 如非必须, 尽量使用goog.object来做吧

参考资料

Tags: clojurescript