Dart 是纯 OO 型语言,所有类型都是 Object
类型的子类型。
变量存储的是引用
变量都是存的引用(而不是值),即使针对数值类型也是如此。可以用 identical
来判断两个变量是否指向同个对象(对象可以看作变量在内存中的位置):
var a = "1";
var b = "1";
print(identical(a, b)); // true
即使 a
和 b
都存的是引用,但是 Dart 做了一个常见的优化,即对于数值类型或者字符串,如果值一样,它在内存中的位置是一样的。所以上面 identical(a, b)
返回的是 true
。
var a = "ab";
var b = "a" + "b";
var c = "ab";
print(identical(a, b)); // false
print(identical(a, c)); // true
这是另外一个例子,可以看出 Dart 在编译时的优化还不够好,不能把 b
直接理解成 "ab"。
另外一点是,对于能在编译期就确定好值的 const
对象,编译器也会把值相同的变量指向同一个对象。
class ImmutablePoint {
final double x, y;
const ImmutablePoint(this.x, this.y);
}
var a = const ImmutablePoint(1, 1);
var b = const ImmutablePoint(1, 1);
print(identical(a, b)); // true
for 循环中的 index 变量作用域
对于这样一段代码:
var callbacks = [];
for (var i = 0; i < 2; i++) {
callbacks.add(() => print(i));
}
callbacks.forEach((c) => c());
如果在 JS 中运行时,会输出 2
和 2
,不合预期;在 Dart 中运行时,会输出 0
和 1
,符合预期。原因是 JS 的 var
关键字使得 i
是函数作用域,所以闭包中的 i
仍然绑定到函数作用域中的同一个 i
对象上,而它的值在循环结束后是 2
。Dart 则不会有这种问题,i
的作用域只在 for 循环中,在 for 循环外不可访问,而且 Dart 会 “capture the value of index”,将其传到闭包中。
对于 JS,解决方法是使用 ES6 中引入的 let
来替代 var
。
类型推断
Dart 虽然是个强类型语言,但非常提倡代码上能简略则简略,比如变量的类型如果是编译期能推断出来的,就不用标上类型:
var name = 'Bob';
String name = 'Bob';
上面两种写法的作用是一样的。
如果你想要一个不限定类型的变量,可以用 dynamic
。这会使 Dart 认为你访问变量的任何方法或属性都是可以的,它不做编译期的检查,但运行期如果调用了不存在的方法,还是会抛 NoSuchMethodError
。
dynamic
对比 Object
的差别在于,使用 Object
时会有编译期检查,使用实际类型的方法或属性时需要做类型转换:
Object p1 = Point(1, 2);
print((p1 as Point).x);
dynamic p2 = Point(1, 2);
print(p2.x);
类型判定及类型推断
// throws exception if emp is not a Person or is null
(emp as Person).firstName = 'Bob';
if (emp is Person) {
// Type check
emp.firstName = 'Bob';
}
除了 is
关键字,还有一个 is!
,跟 is
作用反过来。
默认值
对于 Dart 如果变量没有被赋值,它的值就是 null
;对数值类型亦是如此:
int lineCount;
assert(lineCount == null);
null
的相关操作
为空时赋值:
// Assign value to b if b is null; otherwise, b stays the same
b ??= value;
null
条件判断,如果 expr1
不为 null
,返回 expr1
;否则返回 expr2
:
expr1 ?? expr2
比如:
String playerName(String name) => name ?? 'Guest';
// 等同于
String playerName(String name) => (name ?? 'Guest');
// 等同于
String playerName(String name) {
if (name != null) {
return name;
} else {
return 'Guest';
}
}