Mag*_*uro 6 javascript scala scala.js
我正在尝试scala.js,我必须说它给人留下了深刻的印象!但是,我尝试将它一点一点地引入到我们的生产中,与现有的JavaScript代码并行工作.我正在努力的一件事是将复杂的结构从JS传递到Scala.例如,我有来自其他JS模块的现成JS对象:
h = {
"someInt": 123,
"someStr": "hello",
"someArray": [
{"name": "a book", "price": 123},
{"name": "a newspaper", "price": 456}
],
"someMap": {
"Knuth": {
"name": "The Art of Computer Programming",
"price": 789
},
"Gang of Four": {
"name": "Design Patterns: Blah-blah",
"price": 1234
}
}
}
It
Run Code Online (Sandbox Code Playgroud)
有一些整数,一些字符串(所有这些元素都有固定的键名!),其中有一些数组(其中还有一些对象)和一些映射(将任意字符串键映射到更多对象).一切都是可选的,可能会丢失.显然,它只是一个虚构的例子,现实生活中的对象要复杂得多,但所有的基础都在这里.我已经在Scala中有相应的类层次结构,它看起来像这样:
case class MegaObject(
someInt: Option[Int],
someStr: Option[String],
someArray: Option[Seq[Item]],
someMap: Option[Map[String, Item]]
)
case class Item(name: Option[String], price: Option[Int])
Run Code Online (Sandbox Code Playgroud)
我的第一次尝试是尝试按原样使用接收器类型:
@JSExport
def try1(src: MegaObject): Unit = {
Console.println(src)
Console.println(src.someInt)
Console.println(src.someStr)
}
Run Code Online (Sandbox Code Playgroud)
它显然失败了:
An undefined behavior was detected: [object Object] is not an instance of my.package.MainJs$MegaObject
Run Code Online (Sandbox Code Playgroud)
我的第二个想法是接收这个对象js.Dictionary[String]
,然后进行大量繁琐的类型检查和类型转换.首先,我们将定义一些辅助方法来解析JS对象中的常规字符串和整数:
def getOptStr(obj: js.Dictionary[String], key: String): Option[String] = {
if (obj.contains(key)) {
Some(obj(key))
} else {
None
}
}
def getOptInt(obj: js.Dictionary[String], key: String): Option[Int] = {
if (obj.contains(key)) {
Some(obj(key).asInstanceOf[Int])
} else {
None
}
}
Run Code Online (Sandbox Code Playgroud)
然后我们将使用它们来解析Item
来自同一来源的对象:
def parseItem(src: js.Dictionary[String]): Item = {
val name = getOptStr(src, "name")
val price = getOptInt(src, "price")
Item(name, price)
}
Run Code Online (Sandbox Code Playgroud)
然后,一起解析整个MegaObject
:
@JSExport
def try2(src: js.Dictionary[String]): Unit = {
Console.println(src)
val someInt = getOptInt(src, "someInt")
val someStr = getOptStr(src, "someStr")
val someArray: Option[Seq[Item]] = if (src.contains("someArray")) {
Some(src("someArray").asInstanceOf[js.Array[js.Dictionary[String]]].map { item =>
parseItem(item)
})
} else {
None
}
val someMap: Option[Map[String, Item]] = if (src.contains("someMap")) {
val m = src("someMap").asInstanceOf[js.Dictionary[String]]
val r = m.keys.map { mapKey =>
val mapVal = m(mapKey).asInstanceOf[js.Dictionary[String]]
val item = parseItem(mapVal)
mapKey -> item
}.toMap
Some(r)
} else {
None
}
val result = MegaObject(someInt, someStr, someArray, someMap)
Console.println(result)
}
Run Code Online (Sandbox Code Playgroud)
它,好,工作原理,但它是真的很丑.那是很多代码,很多重复.它可能可以重构以提取数组解析并将解析映射到更健全的东西,但它仍然感觉不好:(
尝试使用@ScalaJSDefined
注释来创建"facade"类的行,如文档中所述:
@ScalaJSDefined
class JSMegaObject(
val someInt: js.Object,
val someStr: js.Object,
val someArray: js.Object,
val someMap: js.Object
) extends js.Object
Run Code Online (Sandbox Code Playgroud)
只需打印出一些作品:
@JSExport
def try3(src: JSMegaObject): Unit = {
Console.println(src)
Console.println(src.someInt)
Console.println(src.someStr)
Console.println(src.someArray)
Console.println(src.someMap)
}
Run Code Online (Sandbox Code Playgroud)
但是,只要我尝试向JSMegaObject"facade"添加一个方法,它就会将它转换为适当的Scala对应物(即使是像这样的伪造的):
@ScalaJSDefined
class JSMegaObject(
val someInt: js.Object,
val someStr: js.Object,
val someArray: js.Object,
val someMap: js.Object
) extends js.Object {
def toScala: MegaObject = {
MegaObject(None, None, None, None)
}
}
Run Code Online (Sandbox Code Playgroud)
尝试调用它失败了:
An undefined behavior was detected: undefined is not an instance of my.package.MainJs$MegaObject
Run Code Online (Sandbox Code Playgroud)
......哪种方式让我想起了#1的尝试.
显然,仍然可以在main方法中完成所有类型转换:
@JSExport
def try3real(src: JSMegaObject): Unit = {
val someInt = if (src.someInt == js.undefined) {
None
} else {
Some(src.someInt.asInstanceOf[Int])
}
val someStr = if (src.someStr == js.undefined) {
None
} else {
Some(src.someStr.asInstanceOf[String])
}
// Think of some way to access maps and arrays here
val r = MegaObject(someInt, someStr, None, None)
Console.println(r)
}
Run Code Online (Sandbox Code Playgroud)
然而,它很快变得像尝试#2一样难看.
所以,我有点沮丧.尝试#2和#3确实有效,但它确实感觉我错过了一些东西而且它应该不那么丑陋,不舒服,并且需要编写大量的JS-to-Scala类型转换器代码才能访问传入的JS对象.有什么更好的方法呢?
你的尝试#4很接近,但并不完全存在.你想要的不是Scala.js定义的JS类.你想要一个真正的门面特征.然后你可以在其伴随对象中"转移"它到你的Scala类的转换.您还必须小心,始终js.UndefOr
用于可选字段.
@ScalaJSDefined
trait JSMegaObject extends js.Object {
val someInt: js.UndefOr[Int]
val someStr: js.UndefOr[String],
val someArray: js.UndefOr[js.Array[JSItem]],
val someMap: js.UndefOr[js.Dictionary[JSItem]]
}
object JSMegaObject {
implicit class JSMegaObjectOps(val self: JSMegaObject) extends AnyVal {
def toMegaObject: MegaObject = {
MegaObject(
self.someInt.toOption,
self.someStr.toOption,
self.someArray.toOption.map(_.map(_.toItem)),
self.someMap.toOption.map(_.mapValues(_.toItem)))
}
}
}
@ScalaJSDefined
trait JSItem extends js.Object {
val name: js.UndefOr[String]
val price: js.UndefOr[Int]
}
object JSItem {
implicit class JSItemOps(val self: JSItem) extends AnyVal {
def toItem: Item = {
Item(
self.name.toOption,
self.price.toOption)
}
}
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
951 次 |
最近记录: |