区别在于抽象定义是抽象的,而具体定义(= js.native)是具体的,从 Scala 的类型系统的角度来看。
然后呢?从类或特征的 use 站点来看,并没有什么不同。这类似于普通的 Scala(或 Java):使用方法时,它是否是抽象的并不重要。
所以真正的区别在于定义站点。理论上,选择抽象还是具体归结为这个标准:
- 此方法在 JavaScript 代码中是否有实际的实现(不仅仅是文档化的合同)?如果是,它应该是具体的;如果不是,它应该是抽象的。
实事求是,注意抽象方法只能出现在抽象类或特征中,并且必须在子类/子特征中实现。
就外观而言,在本机类中,大多数方法应该是具体的(如果不是全部的话)。那是因为在 JS 中,类通常有具体的方法。事实上,abstract 方法在 JS 中甚至都不存在。在本机类中定义抽象方法的唯一合理情况是,如果该类的“合同/文档”规定 a) 它应该是子类,并且 b) 子类应该实现特定的方法(不在超类中实现)。这个记录在案的契约与 JS 可以达到的抽象方法一样接近。
在 JS traits 中,方法通常应该是抽象的(并且 trait 本身是 @ScalaJSDefined 而不是 @js.native)。那是因为特性/接口本身在 JS 中甚至都不存在。它们只存在于其文档化的合同中,合同规定了满足此接口的类必须/将要实现哪些方法。
(@js.native) JS 特征中具体方法的唯一合理用例是用于 DRYness。如果原生 API 的多个类实现了相同(大)的一组方法,那么将这些方法收集到原生 trait 中是合理的。为了不必在所有类中重复它们的定义,它们可以在 trait 中具体化(如果它们是抽象的,类将需要提供一个具体的版本来满足契约)。请注意,非原生 (@ScalaJSDefined) JS 类无法扩展此类特征。
如果您不想弄清楚上述“理论”标准,请使用以下经验法则:
- 该方法是否在原生 JS 类中?如果是,那几乎可以肯定是具体的。
- 它在 JS 特征中吗?如果是,它几乎肯定是抽象的(特征应该是
@ScalaJSDefined)。