Clojurescript: 扩展一个Javascript类

3

我希望使用特定的JavaScript框架,需要扩展基类以在应用程序中使用它。

基本上,我想以惯用的ClojureScript方式执行以下操作。

class Foo extends Bar {
  constructor() { super("data") }
  method1(args) { /* do stuff */ }
}

我尝试过

(defn foo
  []
  (reify
    js/Bar
    (constructor [this] (super this "data"))
    (method1 [this args] )))

如果我从Object创建一个新类,那么这将起作用,但正如shadow-cljs正确地抱怨的那样,“符号js/Bar不是协议”。此外,我不想添加方法,而是创建一个子类,继承某些方法并重载其他方法。

我考虑使用proxy,但“core/proxy未定义”。

当然,我可以创建一个Bar实例并set!新方法,但这感觉就像放弃并使用劣质语言。

2个回答

12

请参见下面的答案,获取更加当前的解决方案!

CLJS没有内置支持class ... extends ...

您可以使用一些样板代码自己进行组合,通过宏生成使其更美观。

(ns your.app
  (:require
    [goog.object :as gobj]
    ["something" :refer (Bar)]))

(defn Foo
  {:jsdoc ["@constructor"]}
  []
  (this-as this
    (.call Bar this "data")
    ;; other constructor work
    this))

(gobj/extend
  (.-prototype Foo)
  (.-prototype Bar)
  ;; defining x.foo(arg) method
  #js {:foo (fn [arg]
              (this-as this
                ;; this is Foo instance
                ))})

谢谢您的回答。很遗憾没有内置的工具可以实现,但最终这将给我一个理由更深入地阅读宏。 - waechtertroll
1
在使用这种方法时,有没有避免“无法调用类构造函数L而不使用'new'”问题的方法?我目前的解决方法是创建包含“export default class Foo extends Bar {}”的存根JS文件,为每个类导入它们,并使用“gobj / extend”来定义CLJS中的实际功能。它可以工作,但JS存根文件夹有点笨重且容易出错。 - Greg
我不确定我以前见过那个错误信息。我知道在尝试扩展本地内置类(例如 HTMLElement)时会出现一些问题,这需要以某种方式使用 Reflect 进行处理,但我记不清细节了。 - Thomas Heller
具体的错误可能是由于我必须使用:output-feature-set:es6来避免Closure编译器错误“尚未实现对'super属性分配'的ES6转换”而导致的库。我将进一步研究使用Reflect,看看它是否可以在这里有所帮助-非常感谢您的指针和快速回复。最坏的情况下,JS存根文件仍然有效,我只是希望我们不会开始看到ES6生态系统把我们落在后面! - Greg

12

CLJS(仍然)没有内置支持 class ... extends ...

但是,在最近的 shadow-cljs 版本中,我添加了对 classextends 的支持。这将生成标准的 JS class,不需要任何 hacky 的解决方法来使其工作。

翻译这个示例:

class Foo extends Bar {
  constructor() { super("data") }
  method1(args) { /* do stuff */ }
}

希望您能

(ns your.app
  (:require [shadow.cljs.modern :refer (defclass)]))

(defclass Foo
  ;; extends takes a symbol argument, referencing the super class
  ;; could be a local symbol such as Bar
  ;; a namespaced symbol such as alias/Bar
  ;; or just a global via js/Bar
  (extends Bar)

  (constructor [this]
    (super "data"))

  ;; adds regular method, protocols similar to deftype/defrecord also supported
  Object
  (method1 [this args]
    ;; do stuff
    ))

这里这里可以找到更复杂的defclass示例。

目前这只能随shadow-cljs一起使用,但你可以从这里获取modern.cljcmodern.cljs文件并将它们放入你的项目中。然后它应该能够与所有的构建工具一起使用。


这很有趣。我正在查看此处的实现,并惊喜地发现CLJS编译器是多么可扩展。在编译器本身原生支持之前,可能需要一段时间(或永远)。您是否考虑将其作为单独的库发布? - kofrasa
目前还没有,但是请随意拿走这两个文件并创建自己的库。只需更改命名空间即可。 - Thomas Heller
如何从重载的method1中调用super.method1()? - royaltm

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接