Scala.js facade for a native JS types can look like this (from 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
/* ... */
}
The corresponding Javascript definition of a function constructing Vector3
is:
function Vector3( x, y, z ) {
this.x = x || 0;
this.y = y || 0;
this.z = z || 0;
}
I have read docs about creating Scala.js facades, however constructors are only briefly mentioned there. The code from the facade works fine in real code, however I am unsure if the definition is correct and why and how it works.
js.native
as default value for all arguments. Should all facades define constructors this way?Esp. the second point is confusing to me. How can this be working? In all three cases I would like to know what JS code is generated for the constructor and why.
One could also imagine a different way how to write the facade. Would that be more correct?
class Vector3(var x: Double = js.native, var y: Double = js.native, var z: Double = js.native) extends Vector {
/* ... */
}
The definition is correct. The rules of facades for JavaScript constructors are quite simply stated: when encountering a call such as
new C(arg1, ..., argN)
and C
is a JavaScript class, this translates to
new Cconstr(arg1, ..., argN)
where Cconstr
is the result of evaluating js.constructorOf[C]
. For a native JavaScript class, js.constructorOf[C]
looks up the name of C
in the global scope (or applies @JSName
or @JSImport
rules). Concretely, in your example, a call such as
new Vector3(3, 4, 5)
translates to
new <global>.THREE.Vector3(3, 4, 5)
Note, in particular, that the body of the constructor definitions is completely irrelevant, since the call site directly calls JavaScript code in the Three.js library. Hence, the fact that the 3-arg constructor calls the 0-arg constructor and ignores its arguments is simply disregarded by the semantic rules. The call is necessary to comply with Scala's type checking rules, but is semantically irrelevant.
Similarly, the actual value of default parameter values are semantically irrelevant. Their presence makes the parameters optional, but their value is otherwise ignored by the compiler. A call such as
new Vector3(3)
translates in JavaScript to
new <global>.THREE.Vector3(3)
in which the y
and z
parameters are altogether not given, leaving to JavaScript to decide what to do with them.
Finally, your alternative definition:
class Vector3(var x: Double = js.native, var y: Double = js.native, var z: Double = js.native)
is just equally valid. It doesn't have an explicit 0-arg constructor, but it can also be "accessed" by giving 0 argument to the constructor, which has 3 optional parameters anyway. This definition is certainly more concise, though, and looks a bit more Scala-esque, so I would personally define it that way. But it is not any more correct than the initial one.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With