Semantics of Scala.js
In general, the semantics of the Scala.js language are the same as Scala on the JVM. However, a few differences exist, which we mention here.
Primitive data types
All nine primitive data types of Scala, i.e.,
Unit, work exactly as on the JVM, with the following three exceptions.
x.toString() returns slightly different results for floating point numbers
In general, a trailing
.0 is omitted.
Floats print in a weird way because they are printed as if they were Doubles,
which means their lack of precision shows up.
To get sensible and portable string representation of floating point numbers,
String.format() or related methods.
Runtime type tests are based on values
Instance tests (and consequently pattern matching) on any of
Double are based on the value and not the
type they were created with. The following are examples:
- 1 matches
- 128 (
> Byte.MaxValue) matches
- 32768 (
> Short.MaxValue) matches
- 2147483647 matches
Double, but not
Float(because that number cannot be represented in a 32-bit
- 2147483648 (
> Int.MaxValue) matches
- 1.5 matches
- 1.4 only matches
Double(unlike 1.5, the value 1.4 cannot be represented in a 32-bit
As a consequence, the following apparent subtyping relationships hold:
Byte <:< Short <:< Int <:< Double <:< Float <:<
In Scala/JVM as well as Scala.js, when assigning a primitive value to an
Any (or a generic type), and asking for its
getClass(), Scala returns the boxed class of the value’s type, rather than the primitive value.
(true: Any).getClass() returns
In Scala.js, for numeric types, and for the same reason that instance tests are based on values, the result will be the smallest boxed class that can store the value.
(5: Any).getClass() will return
(50000: Any).getClass() will return
Scala.js 1.x only:
() (unit), the result will be
classOf[java.lang.Void] instead of
classOf[scala.runtime.BoxedUnit] like the JVM.
scala.runtime.BoxedUnit is an implementation detail of Scala on the JVM, which Scala.js does not emulate.
Instead, it uses the more sensible
Void is the boxed class corresponding to the
void primitive type, which is
This means that while
java.lang.Void is not instantiable on the JVM, in Scala.js it has a singleton instance, namely
This also manifests itself in
Array[Unit] which is effectively
Array[java.lang.Void] at run-time, instead of
Non-strict floats (deprecated; default until Scala.js 1.8.0)
Until v1.8.0, Scala.js underspecified the behavior of
Floats by default with so-called non-strict floats.
Non-strict floats can still be enabled with the following sbt setting:
Under non-strict floats, any
Float value can be stored as a
Double instead, and any operation on
Floats can be computed with double precision.
The choice of whether or not to behave as such, when and where, is left to the implementation.
x.isInstanceOf[Float] will return
true for any
number values (not only the ones that fit in a 32-bit float).
Non-strict floats are deprecated and will eventually be removed in a later major or minor version of Scala.js.
Enabling non-strict floats may significantly improve the performance (up to 4x for
Float-intensive applications) when targeting JS engines that do not support the
Math.fround function, such as Internet Explorer (which implies emitting ES 5.1 code).
If you are in that situation, we advise to use
Doubles instead of
Floats as much as possible.
The JVM is a very well specified environment, which even specifies how some bugs are reported as exceptions. The relevant exceptions are:
Because Scala.js does not receive VM support to detect such erroneous conditions, checking them is typically too expensive.
Therefore, all of these are considered undefined behavior.
Some of these, however, can be configured to be compliant with the JVM specification using sbt settings. Currently, they are:
ClassCastException(thrown by invalid
ArrayIndexOutOfBoundsException(thrown by array indexing)
StringIndexOutOfBoundsException(thrown by string indexing)
The list will probably expand in future versions.
Every configurable undefined behavior has 3 possible modes:
Compliant: behaves as specified on a JVM
Unchecked: completely unchecked and undefined
Fatal: checked, but throws
UndefinedBehaviorErrors instead of the specified exception
By default, undefined behaviors are in
Fatal mode for
fastLinkJS and in
Unchecked mode for
fullOptJS up to Scala.js 1.2.x).
This is so that bugs can be detected more easily during development, with
predictable exceptions and stack traces.
In production code (
fullLinkJS), the checks are removed for maximum
UndefinedBehaviorErrors are fatal in the sense that they are not matched by
case NonFatal(e) handlers.
This makes sure that they always crash your program as early as possible, so
that you can detect and fix the bug.
It is never OK to catch an
UndefinedBehaviorError (other than in a testing
framework), since that means your program will behave differently in
stage than in
If you need a particular kind of exception to be thrown in compliance with the
JVM semantics, you can do so with an sbt setting.
For example, this setting enables compliant
Note that this will have (potentially major) performance impacts.
Java reflection and, a fortiori, Scala reflection, are not supported. There is
limited support for
obj.getClass.getName will work
Regular expressions, as provided by
java.util.regex.Pattern and its derivatives like
scala.util.matching.Regex and the
.r method, are supported, although with some limitations.
More details can be found on the Regular expressions documentation page.
scala.Symbol is supported, but is a potential source of memory leaks
in applications that make heavy use of symbols. The main reason is that
by Scala.js to remain in memory throughout the lifetime of the application.
Value(i: Int) on
reflection to retrieve a string representation of the member name and
are therefore – in principle – unsupported. However, since
Enumerations are an integral part of the Scala library, Scala.js adds
limited support for these two methods:
- Calls to either of these two methods of the forms:
are statically rewritten to (a slightly more complicated version of):
Note that this also includes calls like
since they are desugared into separate
- Calls to either of these two methods which could not be rewritten,
or calls to constructors of the protected
Valclass without an explicit name as parameter, will issue a warning.
Note that the name rewriting honors the
iterator. Therefore, the full rewrite is:
We believe that this covers most use cases of
scala.Enumeration. Please let us know if another (generalized)
rewrite would make your life easier.