1.对 const 的认知
80% 的人对 const 的认知,只停留在 const
修饰的量不可以被修改,这一条。 甚至有些人对什么时候可以用 const 修饰还比较模糊。首先看一下内置的类型变量对象的构造,如 int
、double
、String
,这些类型的对象
可以使用 const 修饰。一旦被 const
修饰的量,就无法再做更改。
2.何时何处可以用 const 修饰
下面先看一下,什么时候能用 const,什么时候不能用 const。如下 Person
类中有一个 name
成员。 对于一个 Person 对象而已,是不能使用 const
修饰的。错误原因,红线处也有提示:
[1]. 无 const 构造方法创建的对象,无法使用 const 修饰。
复制代码
既然提示说,想要 const 修饰对象,其对应构造需要为 const
,那就在构造前加个 const
呗。这时你会发现上面的声明不飘红了,但下面的 const
修饰的构造方法飘红了。提示说 :
[2].使用 const 构造方法的类中,其所有成员属性必须以 final 修饰。
复制代码
所以这时将 name
用 final
修饰,就可以不报错。从上面两点可以得出一个推论:
[推论]: 使用 const 修饰的对象,其各属性也是无法修改的。
复制代码
总结一下 const
可修饰的位置:在声明变量时,可以将 const 放在前面,也可以将 const
放在等号后,修饰构造函数;const
可作为修饰符,修饰构造函数。 只有在修饰 量
的时候,该 量
才不允许被修改。比如下面 toly2
可以赋为其他值,但 toly
不可以。
3.const 在类中的注意点
代码语言:javascript复制[3]: const 构造方法不能有方法体。
复制代码
代码语言:javascript复制[4]: 类中的 const 修饰的成员,必须为 static 静态的。
复制代码
你在源码中可以看到,使用 const
修饰的成员,都会由 static
修饰。
4.const 的价值
如下:a ,b 对象
声明为 const,c 对象
使用普通构造,可见 a 和 b
是全等的,由于没有在 Person
类中重写 ==
运算符,则说明这两个量在 运行时
是同一块内存地址
。这样的好处在于:即使在代码中用了 10000 次 const Person(name: 'toly')
,都是同一对象,都是同一块内存空间。这便是 const 常量的优势,该对象在代码编译期间
就已经确定的。
而对于非 const
创建的对象,用了 10000 次 Person(name: 'toly')
,就是创建了 10000 个对象,每个对象都占着一份空间。 所以这样看来,对于一些不变的常量,使用 const 修饰是很有价值的。
main() {
Person a = const Person(name: 'toly');
Person b = const Person(name: 'toly');
Person c = Person(name: 'toly');
print(a == b); // true
print(a == c); // false
}
复制代码
5. const 常量在 Flutter 中的使用
这时,你再反观 Flutter 中的一些东西,就会有更多的感悟,比如 Text 组件的构造器使用了 const
修饰,就说明 Text 对象可以使用 const 进行修饰,并且 Text
类中的所有属性都必须为 final。
这样,对应不变的文字信息,可以使用 const 进行修饰,这样,下次 build 时这个量就可以直接使用,而非构造新的 Text 对象。Flutter 中的很多组件和属性都有 const 构造,如果它们是不变的,最好使用 const 修饰。
代码语言:javascript复制const Text('张风捷特烈')
复制代码
但要注意:const 是编译期常量,你不能在其中使用运行时的计算,这样编译是无法通过的。
6.关于const 的层级
如下, Padding 及其属性都是常量,作用两边,是否等价?是否需要一一加 const
。
这里来做一个小实验,在 Position
类中添加 Position
属性。 a
和 b
对象构造如下,a 只用一个 const
修饰,b 每个对象都用 const
修饰。结果显示,a 和 b 是相等的
, a.position
和 b.position
也是相等的。这就说明,左右写法是一致的,所以没必要每个对象上都加 const
修饰。
main() {
Person a = const Person(
name: "张风捷特烈",
position: Position(
position: 4
)
);
Person b = const Person(
name: "张风捷特烈",
position: const Position(
position: 4
)
);
print(a == b); // true
print(a.position == b.position); // true
}
class Person {
final String name;
final Position position;
const Person({this.name = '张风捷特烈',this.position});
}
class Position{
final int position;
const Position({this.position = 0});
}
复制代码
越上层使用 const
要求是更严格的,其下都必须都是常量才可以。如下,TextStyle 中 color 使用了 withOpacity
方法,是运行时的,所以构造出的 TextStyle
就不是常量,最上层的 Padding
自然也就不能使用 const
修饰。
同理这里 Text 也无法使用 const
修饰,所以,只能将 const 给 EdgeInsets.all(8.0)
修饰。也就是说,能用 const
修饰的尽量用 const
修饰。
关于 const 在用法上的一些细节点,就讲到这里,FlutterUnit 之前没太注意这方面,现在已经优化完毕。现在你也该想一想,你的常量 const 了吗 ~ 下面小结一下:
代码语言:javascript复制[1]. 无 const 构造方法创建的对象,无法使用 const 修饰。
[2]. 使用 const 构造方法的类中,其所有成员属性必须以 final 修饰。
[推论]: 使用 const 修饰的对象,其各属性也是无法修改的。
[3]. const 构造方法不能有方法体。
[4]: 类中的 const 修饰的成员,必须为 static 静态的。
[5]. const 构造方法中传入的值必须是 const 对象。
复制代码
7. debug 模式下的 Dart 常量去重策略
昨天有人在群里问了 const
为什么在运行时不相等:
因为 debug 模式
下的 Dart 常量去重策略导致的。在官网有相关介绍文章 《调试 Flutter 应用》。其实很容易理解,在 debug
时,需要追踪 Widget
的创建时机,而 const
对象在编译期间就已经初始化了。这是 debug 模式
下的 常量去重策略的必要性。当然你可以通过如下方式来关闭这个 去重策略
,但一般来说这并没有什么必要。
flutter run --no-track-widget-creation
复制代码
如下,在 release
模式下运行,就不会出现这种现象。App
的开发还是以 release
模式为准,所以不必太过纠结。