原生 JS 类型的 Scala.js 外观如下所示(来自 Three.js facade ):
@js.native
@JSName("THREE.Vector3")
class Vector3 extends Vector {
def this(x: Double = js.native, y: Double = js.native, z: Double = js.native) = this()
var x: Double = js.native
var y: Double = js.native
var z: Double = js.native
/* ... */
}
对应Javascript definition构造 Vector3
的函数是:
function Vector3( x, y, z ) {
this.x = x || 0;
this.y = y || 0;
this.z = z || 0;
}
我已阅读有关 creating Scala.js facades 的文档,但是构造函数仅在那里被简单提及。外观中的代码在实际代码中工作得很好,但是我不确定定义是否正确以及它为什么以及如何工作。
- 外观不允许参数构造函数存在。
- 带参数的构造函数只调用无参数的构造函数。尽管如此,该对象似乎构造得很好,成员设置为传递的值。
- 构造函数使用 js.native 作为所有参数的默认值。所有门面都应该这样定义构造函数吗?
特别是。第二点让我感到困惑。这怎么能行得通呢?在这三种情况下,我想知道为构造函数生成了哪些 JS 代码以及原因。
人们还可以想象一种不同的方式来编写外观。这样会更正确吗?
class Vector3(var x: Double = js.native, var y: Double = js.native, var z: Double = js.native) extends Vector {
/* ... */
}
最佳答案
这个定义是正确的。 JavaScript 构造函数的外观规则非常简单:当遇到诸如
之类的调用时new C(arg1, ..., argN)
和 C
是一个 JavaScript 类,这会转换为
new Cconstr(arg1, ..., argN)
其中Cconstr
是评估js.constructorOf[C]
的结果。对于原生 JavaScript 类,js.constructorOf[C]
在全局范围内查找 C
的名称(或应用 @JSName
或 @JSImport
规则)。具体来说,在您的示例中,诸如
new Vector3(3, 4, 5)
翻译为
new <global>.THREE.Vector3(3, 4, 5)
请特别注意,构造函数定义的主体是完全不相关的,因为调用站点直接调用 Three.js 库中的 JavaScript 代码。因此,语义规则完全忽略了 3-arg 构造函数调用 0-arg 构造函数并忽略其参数这一事实。该调用对于遵守 Scala 的类型检查规则是必要的,但在语义上无关。
同样,默认参数值的实际值在语义上也是无关的。它们的存在使得参数可选,但编译器会忽略它们的值。诸如这样的调用
new Vector3(3)
用 JavaScript 翻译为
new <global>.THREE.Vector3(3)
其中完全没有给出 y
和 z
参数,让 JavaScript 来决定如何处理它们。
最后,您的替代定义:
class Vector3(var x: Double = js.native, var y: Double = js.native, var z: Double = js.native)
同样有效。它没有显式的 0-arg 构造函数,但也可以通过向构造函数提供 0 个参数来“访问”它,无论如何,构造函数都有 3 个可选参数。不过,这个定义当然更简洁,而且看起来更像 Scala 风格,所以我个人会这样定义它。但它并不比最初的更正确。
关于Scala.js 原生 Javascript 构造函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41980986/