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 необязательных параметра. Это определение, конечно, более лаконично и выглядит немного более по-скальски, так что я бы лично определил его таким образом. Но это не более правильно, чем первоначальный.

Другие вопросы по тегам