Scala.js нативные конструкторы Javascript
Фасад Scala.js для нативных типов JS может выглядеть следующим образом (из Three.js фасад):
@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 определение построения функции Vector3
является:
function Vector3( x, y, z ) {
this.x = x || 0;
this.y = y || 0;
this.z = z || 0;
}
Я читал документы о создании фасадов Scala.js, но конструкторы там упоминаются лишь кратко. Код с фасада прекрасно работает в реальном коде, однако я не уверен, правильно ли это определение, почему и как оно работает.
- фасад не позволяет использовать конструктор аргументов.
- конструктор с аргументами просто вызывает конструктор без аргументов. Тем не менее, объект, кажется, сконструирован нормально, с членом, установленным на значения переданы.
- конструктор использует
js.native
в качестве значения по умолчанию для всех аргументов. Должны ли все фасады определять конструкторы таким образом?
Особенно Второй момент сбивает меня с толку. Как это может работать? Во всех трех случаях я хотел бы знать, какой код JS генерируется для конструктора и почему.
Можно также представить себе другой способ написания фасада. Это было бы более правильным?
class Vector3(var x: Double = js.native, var y: Double = js.native, var z: Double = js.native) extends Vector {
/* ... */
}
1 ответ
Определение правильное. Правила фасадов для конструкторов 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)
В частности, обратите внимание, что тело определений конструктора не имеет никакого значения, поскольку сайт вызова напрямую вызывает код JavaScript в библиотеке Three.js. Следовательно, тот факт, что 3-аргументный конструктор вызывает 0-аргументный конструктор и игнорирует его аргументы, просто игнорируется семантическими правилами. Вызов необходим для соответствия правилам проверки типов в 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 необязательных параметра. Это определение, конечно, более лаконично и выглядит немного более по-скальски, так что я бы лично определил его таким образом. Но это не более правильно, чем первоначальный.