此页面提供了 JavaScript 和 Scala 编程语言之间的比较。 它适用于了解 JavaScript 并希望了解 Scala 的程序员,特别是通过查看 JavaScript 语言功能与 Scala 比较的示例。
概述
本节对以下各节进行了相对简短的介绍和总结。 它从高层次上介绍了 JavaScript 和 Scala 之间的异同,然后介绍了您在每天编写代码时会遇到的差异。
高层次的相似性
在高层次上,Scala 与 JavaScript 有以下相似之处:
- 两者都被认为是高级编程语言,您不必关心指针和手动内存管理等低级概念
- 两者都有一个相对简单、简洁的语法
- 两者都支持 C/C++/Java 风格的花括号语法,用于编写方法和其他代码块
- 两者都包括面向对象编程 (OOP) 的特性(如类)
- 两者都包含用于 函数式编程 (FP) 的(如 lambda)
- JavaScript 在浏览器和 Node.js 等其他环境中运行。 Scala 的 Scala.js 风格以 JavaScript 为目标,因此 Scala 程序可以在相同的环境中运行。
- 开发人员使用 Node.js 在 JavaScript 和 Scala 中编写服务器端应用程序; Play Framework 之类的项目也可以让您在 Scala 中编写服务器端应用程序
- 两种语言都有相似的
if
语句、while
循环和for
循环 - 从 在这个 Scala.js 页面 开始,您会发现许多支持 React、Angular、jQuery 和许多其他 JavaScript 和Scala 库
- JavaScript 对象是可变的;以命令式风格编写时,Scala 对象_可以_是可变的
- JavaScript 和 Scala 都支持 promises 作为处理异步计算结果的一种方式(Scala concurrency 使用期货和承诺)
高层次差异
同样在高层次上,JavaScript 和 Scala 之间的一些区别是:
- JavaScript 是动态类型的,Scala 是静态类型的
- 尽管 Scala 是静态类型的,但类型推断之类的特性让它感觉像是一种动态语言(正如您将在下面的示例中看到的那样)
- Scala 惯用语默认支持不变性:鼓励您使用不可变变量和不可变集合
- Scala 语法简洁易读;我们称之为_表现力_
- Scala 是一种纯 OOP 语言,因此每个对象都是类的一个实例,而像运算符一样的符号
+
和+=
是真正的方法;这意味着您可以创建自己的方法作为运算符 - 作为一种纯 OOP 语言和纯 FP 语言,Scala 鼓励 OOP 和 FP 的融合,具有用于逻辑的函数和用于模块化的不可变对象
- Scala 拥有最先进的第三方开源函数式编程库
- Scala 中的一切都是一个_表达式_:像
if
语句、for
循环、match
表达式,甚至try
/catch
表达式都有返回值 - Scala Native 项目让您可以编写“系统”级代码,也可以编译为本机可执行文件
编程层次差异
在较低的层次上,这些是您在编写代码时每天都会看到的一些差异:
- Scala 变量和参数使用
val
(不可变,如 JavaScriptconst
)或var
(可变,如 JavaScriptvar
或let
)定义 - Scala 不在行尾使用分号
- Scala 是静态类型的,尽管在许多情况下您不需要声明类型
- Scala 使用 trait 作为接口并创建_混搭_
- 除了简单的
for
循环之外,Scala 还具有强大的for
comprehensions,可以根据您的算法产生结果 - 模式匹配和
match
表达式将改变你编写代码的方式 - Scala 的 Scala 的 上下文抽象 和 术语推导 提供了一系列特性:
- 由于名称参数、中缀符号、可选括号、扩展方法和 高阶函数 等功能,您可以创建自己的“控制结构”和 DSL
- 您可以在本书中阅读到许多其他好东西:样例类、伴生类和对象、宏、[联合][union-type]和交集类型、多参数列表、命名参数等
变量和类型
注释
//
|
//
|
可变变量
let // now preferred for mutable
|
var // used for mutable variables
|
不可变变量
const
|
val
|
Scala 的经验法则是使用 val
声明变量,除非有特定原因需要可变变量。
命名标准
JavaScript 和 Scala 通常使用相同的 CamelCase 命名标准。
变量命名为 myVariableName
,方法命名为 lastIndexOf
,类和对象命名为 Animal
和 PrintedBook
。
字符串
JavaScript 和 Scala 中字符串的许多用法相似,但 Scala 仅对简单字符串使用双引号,对多行字符串使用三引号。
字符串基础
// use single- or double-quotes
|
// use only double-quotes
|
插入
let name = 'Joe';
|
val name = "Joe"
|
带插入的多行字符串
let name = "joe";
|
val name = "Martin Odersky"
|
JavaScript 和 Scala 也有类似的处理字符串的方法,包括 charAt
、concat
、indexOf
等等。
\n
、\f
、\t
等转义字符在两种语言中也是相同的。
数字和算术
JavaScript 和 Scala 之间的数字运算符很相似。
最大的不同是 Scala 不提供 ++
和 --
运算符。
数字运算符:
let x = 1;
|
val x = 1
|
自增和自减:
i++;
|
i += 1;
|
或许最大的区别在于像+
和-
这样的“操作符”在Scala中实际上是_方法_,而不是操作符。
Scala 数字也有这些相关的方法:
var a = 2
a *= 2 // 4
a /= 2 // 2
Scala 的 Double
类型最接近于 JavaScript 的默认 number
类型,
Int
表示有符号的 32 位整数值,而 BigInt
对应于 JavaScript 的 bigint
。
这些是 Scala Int
和 Double
值。
请注意,类型不必显式声明:
val i = 1 // Int
val d = 1.1 // Double
你可以按需要使用其它数字类型:
val a: Byte = 0 // Byte = 0
val a: Double = 0 // Double = 0.0
val a: Float = 0 // Float = 0.0
val a: Int = 0 // Int = 0
val a: Long = 0 // Long = 0
val a: Short = 0 // Short = 0
val x = BigInt(1_234_456_789)
val y = BigDecimal(1_234_456.890)
布尔值
两个语言都在布尔值中用 true
和 false
。
let a = true;
|
val a = true
|
日期
日期是两种语言中另一种常用的类型。
获取当前日期:
let d = new Date();
|
// different ways to get the current date and time
|
指定不同的日期:
let d = Date(2020, 1, 21, 1, 0, 0, 0);
|
val d = LocalDate.of(2020, 1, 21)
|
在这种情况下,Scala 使用 Java 附带的日期和时间类。 JavaScript 和 Scala 之间的许多日期/时间方法是相似的。 有关详细信息,请参阅 java.time 包。
函数
在 JavaScript 和 Scala 中,函数都是对象,因此它们的功能相似,但它们的语法和术语略有不同。
命名函数,一行:
function add(a, b) {
|
// technically this is a method, not a function
|
命名函数,多行:
function addAndDouble(a, b) {
|
def addAndDouble(a: Int, b: Int): Int =
|
在 Scala 中,显示 Int
返回类型是可选的。
它_不_显示在 add
示例中,而_是_显示在 addAndDouble
示例中,因此您可以看到这两种方法。
匿名函数
JavaScript 和 Scala 都允许您定义匿名函数,您可以将其传递给其他函数和方法。
箭头和匿名函数
// arrow function
|
// a function (an anonymous function assigned to a variable)
|
在 Scala 中,您很少使用所示的第一种语法来定义函数。 相反,您经常在使用点定义匿名函数。 许多集合方法是 高阶函数并接受函数参数,因此您编写如下代码:
// map method, long form
List(1,2,3).map(i => i * 10) // List(10,20,30)
// map, short form (which is more commonly used)
List(1,2,3).map(_ * 10) // List(10,20,30)
// filter, short form
List(1,2,3).filter(_ < 3) // List(1,2)
// filter and then map
List(1,2,3,4,5).filter(_ < 3).map(_ * 10) // List(10, 20)
类
Scala 既有类也有样例类。 类 与 JavaScript 类相似,通常用于 OOP 风格应用程序(尽管它们也可以在 FP 代码中使用),并且 _样例类_有附加的特性,这让它在 FP 风格应用中很有用。
下面的例子展示了如何创建几个类型作为枚举,然后定义一个 OOP 风格的 Pizza
类。
最后,创建并使用了一个 Pizza
实例:
// create some enumerations that the Pizza class will use
enum CrustSize:
case Small, Medium, Large
enum CrustType:
case Thin, Thick, Regular
enum Topping:
case Cheese, Pepperoni, BlackOlives, GreenOlives, Onions
// import those enumerations and the ArrayBuffer,
// so the Pizza class can use them
import CrustSize.*
import CrustType.*
import Topping.*
import scala.collection.mutable.ArrayBuffer
// define an OOP style Pizza class
class Pizza(
var crustSize: CrustSize,
var crustType: CrustType
):
private val toppings = ArrayBuffer[Topping]()
def addTopping(t: Topping): Unit =
toppings += t
def removeTopping(t: Topping): Unit =
toppings -= t
def removeAllToppings(): Unit =
toppings.clear()
override def toString(): String =
s"""
|Pizza:
| Crust Size: ${crustSize}
| Crust Type: ${crustType}
| Toppings: ${toppings}
""".stripMargin
end Pizza
// create a Pizza instance
val p = Pizza(Small, Thin)
// change the crust
p.crustSize = Large
p.crustType = Thick
// add and remove toppings
p.addTopping(Cheese)
p.addTopping(Pepperoni)
p.addTopping(BlackOlives)
p.removeTopping(Pepperoni)
// print the pizza, which uses its `toString` method
println(p)
接口、trait 和继承
Scala 使用 trait 作为接口,也可以创建混搭。 trait 可以有抽象和具体的成员,包括方法和字段。
这个例子展示了如何定义两个 traits,创建一个扩展和实现这些 traits 的类,然后创建和使用该类的一个实例:
trait HasLegs:
def numLegs: Int
def walk(): Unit
def stop() = println("Stopped walking")
trait HasTail:
def wagTail(): Unit
def stopTail(): Unit
class Dog(var name: String) extends HasLegs, HasTail:
val numLegs = 4
def walk() = println("I’m walking")
def wagTail() = println("⎞⎜⎛ ⎞⎜⎛")
def stopTail() = println("Tail is stopped")
override def toString = s"$name is a Dog"
// create a Dog instance
val d = Dog("Rover")
// use the class’s attributes and behaviors
println(d.numLegs) // 4
d.wagTail() // "⎞⎜⎛ ⎞⎜⎛"
d.walk() // "I’m walking"
控制结构
除了在 JavaScript 中使用 ===
和 !==
之外,比较和逻辑运算符在 JavaScript 和 Scala 中几乎相同。
比较运算符
JavaScript | Scala |
---|---|
== |
== |
=== |
== |
!= |
!= |
!== |
!= |
> |
> |
< |
< |
>= |
>= |
<= |
<= |
逻辑运算符
JavaScript | Scala |
---|---|
&&
|
&&
|
if/then/else 表达式
JavaScript和 Scala if/then/else 语句相似。 在 Scala 2 中它们几乎相同,但在 Scala 3 中,花括号不再是必需的(尽管它们仍然可以使用)。
if
语句,单行:
if (x == 1) { console.log(1); }
|
if x == 1 then println(x)
|
if
语句,多行:
if (x == 1) {
|
if x == 1 then
|
if, else if, else:
if (x < 0) {
|
if x < 0 then
|
从 if
返回值:
JavaScript 使用三元运算符,Scala 像往常一样使用它的 if
表达式:
let minVal = a < b ? a : b;
|
val minValue = if a < b then a else b
|
if
作为方法体:
Scala 方法往往很短,您可以轻松地使用 if
作为方法体:
function min(a, b) {
|
def min(a: Int, b: Int): Int =
|
在 Scala 3 中,如果您愿意,您仍然可以使用“花括号”样式。 例如,您可以像这样编写 if/else-if/else 表达式:
if (i == 0) {
println(0)
} else if (i == 1) {
println(1)
} else {
println("other")
}
循环
JavaScript 和 Scala 都有 while
循环和 for
循环。
Scala 曾经有 do/while 循环,但它们已从语言中删除。
while
循环:
let i = 0;
|
var i = 0;
|
如果你愿意,Scala 代码也可以写成这样:
var i = 0
while (i < 3) {
println(i)
i += 1
}
以下示例展示了 JavaScript 和 Scala 中的“for”循环。 他们假设您可以使用这些集合:
// JavaScript
let nums = [1, 2, 3];
// Scala
val nums = List(1, 2, 3)
for
循环,单行:
// newer syntax
|
// preferred
|
for
循环,在循环体内多行
// preferred
|
// preferred
|
在 for
循环中有多个生成器
let str = "ab";
|
for
|
带守卫的生成器
_守卫_是 for
表达式中的 if
表达式的名称。
for (let i = 0; i < 10; i++) {
|
for
|
for
comprehension
for
comprehension 是一个 for
循环,它使用 yield
返回(产生)一个值。 它们经常在 Scala 中使用。
N/A |
val list =
|
switch & match
JavaScript 有 switch
语句,Scala 有 match
表达式。
就像 Scala 中的所有其他东西一样,这些确实是_表达式_,这意味着它们返回一个结果:
val day = 1
// later in the code ...
val monthAsString = day match
case 1 => "January"
case 2 => "February"
case _ => "Other"
match
表达式可以在每个 case
语句中处理多个匹配项:
val numAsString = i match
case 1 | 3 | 5 | 7 | 9 => "odd"
case 2 | 4 | 6 | 8 | 10 => "even"
case _ => "too big"
它们也可以用于方法体:
def isTruthy(a: Matchable) = a match
case 0 | "" => false
case _ => true
def isPerson(x: Matchable): Boolean = x match
case p: Person => true
case _ => false
match
表达式有许多其他模式匹配选项。
集合类
Scala 有不同的 集合类 来满足不同的需求。
常见的_不可变_序列是:
List
Vector
常见的_可变_序列是:
Array
ArrayBuffer
Scala 还有可变和不可变的 Map 和 Set。
这是创建常见 Scala 集合类型的方式:
val strings = List("a", "b", "c")
val strings = Vector("a", "b", "c")
val strings = ArrayBuffer("a", "b", "c")
val set = Set("a", "b", "a") // result: Set("a", "b")
val map = Map(
"a" -> 1,
"b" -> 2,
"c" -> 3
)
集合上的方法
以下示例展示了使用 Scala 集合的许多不同方法。
填充列表:
// to, until
(1 to 5).toList // List(1, 2, 3, 4, 5)
(1 until 5).toList // List(1, 2, 3, 4)
(1 to 10 by 2).toList // List(1, 3, 5, 7, 9)
(1 until 10 by 2).toList // List(1, 3, 5, 7, 9)
(1 to 10).by(2).toList // List(1, 3, 5, 7, 9)
('d' to 'h').toList // List(d, e, f, g, h)
('d' until 'h').toList // List(d, e, f, g)
('a' to 'f').by(2).toList // List(a, c, e)
// range method
List.range(1, 3) // List(1, 2)
List.range(1, 6, 2) // List(1, 3, 5)
List.fill(3)("foo") // List(foo, foo, foo)
List.tabulate(3)(n => n * n) // List(0, 1, 4)
List.tabulate(4)(n => n * n) // List(0, 1, 4, 9)
序列上的函数式方法:
// these examples use a List, but they’re the same with Vector
val a = List(10, 20, 30, 40, 10) // List(10, 20, 30, 40, 10)
a.contains(20) // true
a.distinct // List(10, 20, 30, 40)
a.drop(2) // List(30, 40, 10)
a.dropRight(2) // List(10, 20, 30)
a.dropWhile(_ < 25) // List(30, 40, 10)
a.filter(_ < 25) // List(10, 20, 10)
a.filter(_ > 100) // List()
a.find(_ > 20) // Some(30)
a.head // 10
a.headOption // Some(10)
a.init // List(10, 20, 30, 40)
a.last // 10
a.lastOption // Some(10)
a.slice(2,4) // List(30, 40)
a.tail // List(20, 30, 40, 10)
a.take(3) // List(10, 20, 30)
a.takeRight(2) // List(40, 10)
a.takeWhile(_ < 30) // List(10, 20)
// map, flatMap
val fruits = List("apple", "pear")
fruits.map(_.toUpperCase) // List(APPLE, PEAR)
fruits.flatMap(_.toUpperCase) // List(A, P, P, L, E, P, E, A, R)
val nums = List(10, 5, 8, 1, 7)
nums.sorted // List(1, 5, 7, 8, 10)
nums.sortWith(_ < _) // List(1, 5, 7, 8, 10)
nums.sortWith(_ > _) // List(10, 8, 7, 5, 1)
List(1,2,3).updated(0,10) // List(10, 2, 3)
List(2,4).union(List(1,3)) // List(2, 4, 1, 3)
// zip
val women = List("Wilma", "Betty") // List(Wilma, Betty)
val men = List("Fred", "Barney") // List(Fred, Barney)
val couples = women.zip(men) // List((Wilma,Fred), (Betty,Barney))
Scala 有_很多_更多可供您使用的方法。 所有这些方法的好处是:
- 您不必编写自定义的
for
循环来解决问题 - 当你阅读别人的代码时,你不必阅读他们自定义的
for
循环; 你只会找到像这样的常用方法,因此更容易阅读来自不同项目的代码
元组
当您想将多个数据类型放在同一个列表中时,JavaScript 允许您这样做:
stuff = ["Joe", 42, 1.0];
在 Scala 中你这样做:
val a = ("eleven")
val b = ("eleven", 11)
val c = ("eleven", 11, 11.0)
val d = ("eleven", 11, 11.0, Person("Eleven"))
在 Scala 中,这些类型称为元组,如图所示,它们可以包含一个或多个元素,并且元素可以具有不同的类型。
访问它们的元素就像访问 List
、Vector
或 Array
的元素一样:
d(0) // "eleven"
d(1) // 11
枚举
JavaScript 没有枚举,但你可以这样做:
let Color = {
RED: 1,
GREEN: 2,
BLUE: 3
};
Object.freeze(Color);
在 Scala 3 中,您可以使用枚举做很多事情。 您可以创建该代码的等效代码:
enum Color:
case Red, Green, Blue
你可以创建带参数的枚举:
enum Color(val rgb: Int):
case Red extends Color(0xFF0000)
case Green extends Color(0x00FF00)
case Blue extends Color(0x0000FF)
你也可以创建用户自定义的枚举成员:
enum Planet(mass: Double, radius: Double):
case Mercury extends Planet(3.303e+23, 2.4397e6)
case Venus extends Planet(4.869e+24,6.0518e6)
case Earth extends Planet(5.976e+24,6.37814e6)
// more planets here ...
private final val G = 6.67300E-11
def surfaceGravity = G * mass / (radius * radius)
def surfaceWeight(otherMass: Double) = otherMass * surfaceGravity
Scala.js DOM 代码
Scala.js 允许您编写 Scala 代码,这些代码编译为 JavaScript 代码,然后可以在浏览器中使用。 该方法类似于 TypeScript、ReScript 和其他编译为 JavaScript 的语言。
包含必要的库并在项目中导入必要的包后,编写 Scala.js 代码看起来与编写 JavaScript 代码非常相似:
// show an alert dialog on a button click
jQuery("#hello-button").click{() =>
dom.window.alert("Hello, world")
}
// define a button and what should happen when it’s clicked
val btn = button(
"Click me",
onclick := { () =>
dom.window.alert("Hello, world")
})
// create two divs with css classes, an h2 element, and the button
val content =
div(cls := "foo",
div(cls := "bar",
h2("Hello"),
btn
)
)
// add the content to the DOM
val root = dom.document.getElementById("root")
root.innerHTML = ""
root.appendChild(content.render)
请注意,尽管 Scala 是一种类型安全的语言,但在上面的代码中没有声明任何类型。 Scala 强大的类型推断能力通常使 Scala 代码看起来像是动态类型的。 但它是类型安全的,因此您可以在开发周期的早期捕获许多类错误。
其他 Scala.js 资源
Scala.js 网站为对使用 Scala.js 感兴趣的 JavaScript 开发人员提供了极好的教程集。 以下是他们的一些初始教程:
- 基础教程(创建第一个 Scala.js 项目)
- 适用于 JavaScript 开发人员的 Scala.js
- 从 ES6 到 Scala:基础
- 从 ES6 到 Scala:集合
- 从 ES6 到 Scala:高级
Scala 独有的概念
Scala 中还有其他一些概念目前在 JavaScript 中没有等效的概念:
Contributors to this page:
Contents
- 导言
- Scala 3 特性
- 为什么是 Scala 3 ?
- Scala 的味道
- Hello, World!
- The REPL
- 变量和数据类型
- 控制结构
- 领域建模
- 方法
- 头等函数
- 单例对象
- 集合
- 上下文抽象
- 顶层定义
- 总结
- 类型初探
- 字符串插值
- 控制结构
- 领域建模
- 工具
- OOP 领域建模
- 函数式领域建模
- 方法
- 方法特性
- main 方法
- 总结
- 函数
- 匿名函数
- 函数变量
- Eta 扩展
- 高阶函数
- 自定义 map 函数
- 创建可以返回函数的方法
- 总结
- 打包和导入
- Scala 集合
- 集合类型
- 集合方法
- 总结
- 函数式编程
- 什么是函数式编程?
- 不可变值
- 纯函数
- 函数是值
- 函数式错误处理
- 总结
- 类型和类型系统
- 类型推断
- 泛型
- 相交类型
- 联合类型
- 代数数据类型
- 型变
- 不透明类型
- 结构化类型
- 依赖函数类型
- 其他类型
- 上下文抽象
- 扩展方法
- Given 实例和 Using 语句
- 上下文绑定
- Given 导入
- 实现类型类
- 多元相等性
- 隐式转换
- 总结
- 并发
- Scala 工具
- 使用 sbt 构建和测试 Scala 项目
- worksheet
- 与 Java 交互
- 向 Java 开发者介绍Scala
- Scala for JavaScript Developers
- Scala for Python Developers
- 下一步去哪