如何在ClojureScript中实现静态属性及模拟JavaScript继承?
先来看你给出的原始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

