Dart操作符

1,850 阅读5分钟

Dart定义下表中显示的运算符。你可以覆盖其中很多运算符,如可覆盖运算符图中所示:

深度截图_选择区域_20190811155244.png

你可以覆盖下表中显示的运算符(具体使用在介绍Dart中的类时会介绍):
深度截图_选择区域_20190811155322.png

使用运算符创建表达式,下面是一些例子:

// 下面这些都是表达式
a++
a + b
a = b
a == b
c ? a : b
a is T

在第一张表中,每一行的运算符优先级都高于它下面的行中的运算符。例如,的优先级高于(因此更早执行)==,它们两的优先级又高于逻辑与运算符&&。该优先级意味着以下两行代码执行结果一致:

// 括号提高了可读性
if ((n % i == 0) && (d % i == 0)) ...

// 和上面的语句等价,但是不易读懂
if (n % i == 0 && d % i == 0) ...

算数操作符

基本使用:

assert(2 + 3 == 5);
assert(2 - 3 == -1); 
assert(2 * 3 == 6);
assert(5 / 2 == 2.5); // 结果是double类型
assert(5 ~/ 2 == 2); // 整除 结果是int类型
assert(5 % 2 == 1); // 求余

Dart还支持前置和后置增量和减量运算符:

var a, b;

a = 0;
b = ++a; // 赋值前自增
assert(a == b); // 1 == 1

a = 0;
b = a++; // 赋值后自增
assert(a != b); // 1 != 0

a = 0;
b = --a; // 赋值前自减
assert(a == b); // -1 == -1

a = 0;
b = a--; // 复制后自减
assert(a != b); // -1 != 0

等于和相关操作符

要测试两个objectx和y是否表示相同的事物,请使用==运算符。(在极少数情况下,你需要知道两个对象是否完全相同,请使用 identical()函数来代替。)以下是==运算符的工作原理:

  1. 如果两个都为null,返回true,如果只有一个为null返回false
  2. 返回调用x.==(y)的结果。(没错,运算符如==是在第一个操作数上调用的方法。前面提到了,你可以覆盖许多运算符,包括==,具体使用在介绍Dart中的类时会介绍,详情:Overridable operators。)。

典型示例:

assert(2 == 2);
assert(2 != 3);
assert(3 > 2);
assert(2 < 3);
assert(3 >= 3);
assert(2 <= 3);

类型测试操作符

asisis!运算符在运行时检查类型很方便。
obj is T的结果取决于obj是否实现了T的接口,我们知道Dart中都是object,所以obj is Object总是返回true。(is!的用法和is相反)
当我们不确定某个变量是不是实现了某个类的接口时,需要判断一下,然后再使用:

if (emp is Person) {
  // Type check
  emp.firstName = 'Bob';
}

as操作符可以所写这个判断:

(emp as Person).firstName = 'Bob';

这两种写法不完全相同。如果**emp****null**或不是**Person**,则第一个示例(用 **is**)不执行任何操作;第二个(用**as**)抛出一个异常。

赋值操作符

也就是给变量赋值的操作符(=):

// 赋值给变量a
a = value;
// 只有当b为空时才赋值给b,否则保持b的值不变。
b ??= value;

赋值运算符,把赋值和运算操作整合到一起,以下是常见的赋值运算符:

= –= /= %= >>= ^=
+= *= ~/= <<= &= |=
var a = 2; // 普通的赋值
a *= 3; // 等价于 a = a * 3
assert(a == 6);

逻辑运算符

可以使用逻辑运算符反转或组合布尔表达式:

操作符 含义
!_expr_ 反转bool值
|| 逻辑或
&& 逻辑与

例子:

if (!done && (col == 0 || col == 3)) {
  // ...Do something...
}

位操作符

你可以在Dart中操纵数字的二进制位。通常,您将使用对整数使用这些操作符:

final value = 5; // 二进制的101 前面的0这里省略显示了
final bitmask = 3; // 二进制的011 前面的0这里省略显示了

// 按位与,都是1返回1,其它返回0
print(value & bitmask); // 二进制的001 也就是1
// 按位或,只要有一个1返回1,都是0返回0
print(value | bitmask); // 二进制的111 也就是7

由于位操作符不太常用,其它用法大家可以自行查找,这里不在赘述。

条件表达式

三元表达式:

// isPublic为true,返回public,反之返回private
var visibility = isPublic ? 'public' : 'private';

简单的判断是否为空值(null):

// 传入的name不为空时返回name,没有传入返回'Guest';
String playerName(String name) => name ?? 'Guest';
// 等价于以下写法
// 用三元表达式替换
String playerName(String name) => name != null ? name : 'Guest';
// 用if else替换
String playerName(String name) {
  if (name != null) {
    return name;
  } else {
    return 'Guest';
  }
}

级联操作符

Cascades(..)允许你对同一对象进行一系列操作。除了函数调用,还可以访问同一对象上的字段。这可以省略穿件临时变量,也让代码更好看:

// 这里的两个操作和一个取值赋值,都在querySelector('#confirm')上面操作
// 级联操作符会忽略每一个子句的返回值。
querySelector('#confirm') // Get an object.
  ..text = 'Confirm' // Use its members.
  ..classes.add('important')
  ..onClick.listen((e) => window.alert('Confirmed!'));
// 等价于
var button = querySelector('#confirm');
button.text = 'Confirm';
button.classes.add('important');
button.onClick.listen((e) => window.alert('Confirmed!'));

嵌套级联操作:

final addressBook = (AddressBookBuilder()
      ..name = 'jenny'
      ..email = 'jenny@example.com'
      ..phone = (PhoneNumberBuilder() // 这里的PhoneNumberBuilder()在number和label赋值后,返回给phone
            ..number = '415-555-0100'// 返回给phone的是PhoneNumberBuilder()
            ..label = 'home')
          .build())
    .build();

注意使用级联操作符时,一定要对一个实际的object上执行:

var sb = StringBuffer();
sb.write('foo')
  ..write('bar'); // Error: method 'write' isn't defined for 'void'.
// sb.write('foo')返回的是void。你不能在void上面使用级联操作符

严格来说级联操作符并不是操作符,二十Dart语法的一部分。

其他常用的操作符

操作符 说明
() 执行方法
[] list的索引访问
. 表达式的属性访问
例如: foo.bar  从 foo取 bar
?. 类似 ., 但是左边的表达式为空时不会报错,而是返回空。
例子: foo?.bar  从 foo取 bar ,如果foo为null,那么foo?.bar 返回空。