You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在ClojureScript中实现静态属性及模拟JavaScript继承?

解决ClojureScript中模拟JS类getter与继承EventEmitter静态属性的问题

先来看你给出的原始JavaScript实现:

class AccessController extends EventEmitter {
  static async create (db, options) { }
  static get type () { throw new Error('\'static get type ()\' needs to be defined in the inheriting class') }
  get type () { return this.constructor.type }
  async canAppend (entry, identityProvider) { return true }
}
class OtherController extends AccessController {
  constructor (db, options) { super() }
  static async create (db, options) { return new OtherController (db, options) }
  static get type () { return 'othertype' }
  async canAppend(entry, identityProvider) { return true }
}

还有你尝试的ClojureScript代码:

(defn- OtherController {:jsdoc ["@constructor"]} [orbitdb options]
  (this-as this
    (.call AccessController this orbitdb options)
    this))
(defn create-access-controller []
  (gobj/extend
    ;; inheritance
    (.-prototype OtherController)
    (.-prototype AccessController)
    ;; methods
    #js {:canAppend (fn [entry identity-provider] true)})
  ;; static properties
  (set! (.. OtherController -type) "othertype")
  (set! (.. OtherController -create) (fn [db options] (new OtherController db (clj->js {}))))
  OtherController)

针对你不确定的两个点,我来逐一说明:

1. 模拟JavaScript的get语法糖

ClojureScript里没有JS那种原生的getter语法糖,不过咱们可以用js/Object.defineProperty来手动实现完全一致的效果,不管是实例属性的getter还是静态属性的getter都适用。

实例级别的type getter

对应JS里get type () { return this.constructor.type }这段,咱们需要在OtherController的原型上定义一个getter:

;; 替换你原来的gobj/extend里的方法定义部分
(gobj/extend
  (.-prototype OtherController)
  (.-prototype AccessController))

;; 定义实例的type getter
(js/Object.defineProperty (.-prototype OtherController) "type"
  #js {:get (fn [] (.-type (.-constructor this)))})

这样当你创建OtherController的实例后,访问(.type instance)时,就会触发这个getter函数,返回构造函数的静态type值。

静态级别的type getter

对应JS里static get type () { return 'othertype' },直接在OtherController构造函数上用Object.defineProperty定义:

(js/Object.defineProperty OtherController "type"
  #js {:get (fn [] "othertype")})

和JS里的行为完全一致,当你访问(.-type OtherController)时,会调用这个getter返回值,而不是直接读取一个固定属性。如果后续需要修改返回逻辑,直接改这个getter函数就行。

2. 继承EventEmitter的静态属性

如果EventEmitter有静态属性或方法需要继承,咱们需要把这些静态成员从EventEmitter复制到AccessController,再复制到OtherController(因为AccessController本身继承自EventEmitter)。

最简单的方式是用gobj/extend批量复制:

;; 先让AccessController继承EventEmitter的静态属性(如果之前没做的话)
(gobj/extend AccessController js/EventEmitter)

;; 再让OtherController继承AccessController的所有静态属性
(gobj/extend OtherController AccessController)

这样OtherController就会拥有EventEmitter上的所有静态成员了。要是你只想继承特定的静态方法/属性,也可以手动用set!逐个赋值:

;; 比如只继承EventEmitter的某个静态方法
(set! (.. OtherController -someStaticMethod) (.. js/EventEmitter -someStaticMethod))

最后整合一下完整的代码:

(defn- OtherController {:jsdoc ["@constructor"]} [orbitdb options]
  (this-as this
    (.call AccessController this orbitdb options)
    this))

(defn create-access-controller []
  ;; 让AccessController继承EventEmitter的静态属性
  (gobj/extend AccessController js/EventEmitter)
  
  ;; 让OtherController继承AccessController的静态属性
  (gobj/extend OtherController AccessController)
  
  ;; 处理原型继承
  (gobj/extend
    (.-prototype OtherController)
    (.-prototype AccessController))
  
  ;; 定义实例的type getter
  (js/Object.defineProperty (.-prototype OtherController) "type"
    #js {:get (fn [] (.-type (.-constructor this)))})
  
  ;; 定义canAppend实例方法
  (set! (.. OtherController -prototype -canAppend)
        (fn [entry identity-provider] true))
  
  ;; 定义静态的type getter
  (js/Object.defineProperty OtherController "type"
    #js {:get (fn [] "othertype")})
  
  ;; 定义静态create方法
  (set! (.. OtherController -create)
        (fn [db options] (new OtherController db (clj->js options))))
  
  OtherController)

内容的提问来源于stack exchange,提问作者fbielejec

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 18:12:42