我在 JetBrains 论坛上的一位 JetBrains 人员 Alexey Andreev 那里得到了很好的回答,网址为https://discuss.kotlinlang.org/t/controlling-the-jsname-of-fields-for-pouchdb-interop/2531 /。在我描述之前,我将提到进一步失败的改进@bashor 答案的尝试。
属性委托
我认为@bashor 的回答是使用属性委托,但如果没有无限递归,我就无法让它工作。
class JSMapDelegate<T>(
val jsobject: dynamic
) {
operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
return jsobject[property.name]
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
jsobject[property.name] = value
}
}
external interface PouchDoc4 {
var _id: String
var _rev: String
}
class Impl4() : PouchDoc4 {
override var _id: String by JSMapDelegate<String>(this)
override var _rev: String by JSMapDelegate<String>(this)
constructor(_id: String) : this() {
this._id = _id
}
}
委托中的jsobject[property.name] = value
调用调用属性的 set 函数,该函数再次调用委托...
(此外,事实证明,您不能将委托放在接口中的属性上,即使您可以定义像委托一样工作的 getter/setter 对,如 @bashor 的PouchDoc2
示例所示。)
使用外部类
Alexey 在 Kotlin 论坛上的回答基本上是说:“您正在混合业务(与行为)和持久性(仅数据)层:正确的答案是显式序列化到 JS 或从 JS 序列化,但我们还没有提供;因为一种解决方法,使用外部类。” 我认为,关键是外部类不会变成定义属性 getter/setter 的 JavaScript,因为 Kotlin 不允许您定义外部类的行为。鉴于这种指导,我得到了以下工作,这就是我想要的。
external interface PouchDoc5 {
var _id: String
var _rev: String
}
external class Impl5 : PouchDoc5 {
override var _id: String
override var _rev: String
}
fun <T> create(): T = js("{ return {}; }")
fun Impl5(_id: String): Impl5 {
return create<Impl5>().apply {
this._id = _id
}
}
keys
为此的输出是
null
instance keys: _id
__proto__ keys: toSource,toString,toLocaleString,valueOf,watch,unwatch,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,__defineGetter__,__defineSetter__,__lookupGetter__,__lookupSetter__,__proto__,constructor
创建外部类
关于创建外部类实例的三个注意事项。首先,Alexey 说要写
fun <T> create(): T = js("{}")
但对我来说(使用 Kotlin 1.1)变成
function jsobject() {
}
其返回值为undefined
。我认为这可能是一个错误,因为官方文档也建议使用较短的形式。
其次,你不能这样做
fun Impl5(_id: String): Impl5 {
return (js("{}") as Impl5).apply {
this._id = _id
}
}
因为这显式地插入了一个类型检查Impl5
,它会抛出ReferenceError: Impl5 is not defined
(至少在 Firefox 中)。通用函数方法跳过类型检查。我猜这不是错误,因为 Alexey 推荐了它,但它看起来很奇怪,所以我会问他。
最后,您可以标记create
为inline
,尽管您需要禁止显示警告:-)