theme: cyanosis
一、 Patterns 是什么
下面是官方对 Patterns 特性的说明 patterns :\ 从下面的第一句中可以知道,Patterns 是一种语法级的特性,而语法特性是一种语言的根基。
Patterns are a syntactic category in the Dart language, like statements and expressions.\ Patterns 是 Dart 语言中的一个语法类别,就像语句和表达式一样。
从下面第二句话在可以看出,Pattern 和数据的特征匹配相关。
A pattern represents the shape of a set of values that it may match against actual values.\ Pattern 表示可能与实际值相匹配的一组值的特征。
在英文中 Pattern 一词有:模式、形式、图样的意思。说句题外话:String 字符串和 Regex 正则表达式都实现 Pattern 接口,就说明 Pattern 一词和模式匹配的渊源。这里强调一句:Dart 3.0 的 Patterns 语法和上面提及的 Pattern 类型没有半毛钱关系。
在日常开发中,我们使用的类型都是具有一定的结构特征,而结构正是类中数据的栖身之地。Patterns 像是一种在语法层面,对类型结构特征提取的规则,结合匹配来更方便地完成一些工作。在类型之中, Record、List、Map 三种类型,有着非常明显的结构特征:
```dart 记录 Record 类型 |--- 普通值 (v1, v2 ,...) |--- 命名值 (k1:v1,k2:v2 ,...)
列表 List 类型 |--- 值列表 [v1,v2,...]
映射 Map 类型 |--- 键值对 {k1:v1, k2:v2 ,...} ```
二、 Patterns 的解构能力
解构(Destructuring) 就是访问并提取对象的某些数据,为某些指定的变量进行赋值的过程。其中提取数据就需要运用到 Patterns 的匹配特性。下面通过几个小例子了解一下:
1.对 Record 类型的解构
- 非命名 Record 的类型
如下 foo 中 : 默认情况下,想要访问记录对象中的数据,需要通过 $1
和 $2
:
dart void foo(){ var user = ('toly',29); String name = user.$1; int age = user.$2; print('======$name====${age}==='); }
如下 foo1 中 : 可以使用 Patterns 的特性,直接将 user 对象解构,为 name
和 age
赋值。这就是 Patterns 最重要的能力之一,
dart void foo1() { var user = ('toly',29); var (name, age) = user; // 直接解构对象 print('======$name====${age}==='); }
Record 的解构语法是 :
var
(
变量 1 , 变量 2 , ...)
= Record 对象
- 命名 Record 类型
对于元素被命名的 Record 类型而言, 可以通过如下 Patterns 语法进行解构。比如下面,一句代码就可以调试为 a
、b
、c
三个变量赋值:
dart var position = (x:1,y:3, 'p0'); var (x:a, y:b ,c) = position; print('====$a====$b====$c====');
var
(
k1: 变量 1, k2: 变量 2 , ...)
= Record 对象
对于命名的数值而言,可以通过 :key
进行简写。比如下面的 :x
含义就是 x:x
,表示:将右侧对象中的名称为 x 的数据,为左侧的 x 变量赋值。
dart var position = (x:1,y:3, 'p0'); var (:x,:y,d) = position; print('====$x====$y====$d====');
这样的好处是能少起个变量名,适合 起名困难症者;但与此同时,这样你无法为变量名起别的名字。
2. 对 List 和 Map 的解构
除了 Record 类型 ,还有 List 和 Map 也支持解构。效果上类似,都是访问对象的数据,并直接为变量赋值。List 的结构语法是 :
var
[
变量1, 变量2, ...]
= List 对象
dart void foo2(){ List<int> numList = [1, 2, 3]; var [a, b, c] = numList; print('====$a====$b====$c='); }
如下是对 Map 对象的解构,语法是:
var
{
key : 变量1, key: 变量2, ...}
= Map 对象
dart void foo3(){ Map<String,dynamic> data = { 'name': 'toly', 'age': 29, }; var {'name': name,'age': age}= data; print('======$name====${age}==='); }
同理,对于 Map 元素组成的 List 列表,也可以通过对应的语法进行解构,只要左侧变量结构符合右侧对象结构即可 :
dart void foo4(){ var data = [ { 'name': 'toly', 'age': 29, }, { 'name': 'ls', 'age': 28, }, ]; var [{'name': name,'age': age},{'name': name1,'age': age2}] = data; print('======$name====${age}===$name1====${age2}===='); }
3. 对普通对象的解构
除了可以解构特定的对象之外,还可以对普通对象进行解构,但要注意 只有构造函数中的命名参数字段支持解构。如下所示,定义了 Person
类:
```dart class Person { final String name; final int age;
Person({ required this.name, required this.age, }); } ```
对象结构语法为:
var
类名(
命名字段1 : 变量1 , 命名字段2 : 变量2, ...)
= 对象
dart void foo5(){ Person person = Person(name: 'toly', age: 29); var Person(name : a, age: b) = person; print('======$a====${b}==='); }
同样,如果懒得为变量起名字,也可以直接让字段名称为变量名:
var
类名(
: 命名字段1 , : 命名字段2, ...)
= 对象
dart void foo5(){ Person person = Person(name: 'toly', age: 29); var Person(:name, :age) = person; print('======$name====${age}==='); }
三、解构时需要注意的问题
1、解构结构的一致性
首先要注意的是,对于 List 、Record、Map 对象来说,左侧的解构结构要和对象数据结构 完全一致。 比如下面列表有三个元素,你只解构了两个,在运行时会报错。我觉得比较坑的是:
如果不一致的话,在
编辑期间
无法发觉,问题只能在运行时
暴露,这就或多或少存在一定的代码隐患。
同理,如果在解构 Map 对象时 key 写错了,在运行时也会报错:
2、忽略解构单元
List 、Record、Map 对象的解构需要保持结构的一致性,但有时候并不需要完全结构所有的数据,此时可以使用 _
来忽略对应的结构单元。
dart void foo8() { List<int> numList = [1, 2, 3]; var [first, _,_] = numList; print('====$first===='); }
3、小结
本文从 解构 的角度,认识了一些常用类型的 Pattern 语法,下图是一个小结:
从这里我们或多或少可以体会出 Patterns 是一种 对模式的匹配
。而解构是运用模式匹配的能力,从对象中提取数据为对应变量赋值。我们一开始就说了 Patterns 是一种语法级的特性,解构只是它的作用之一。而且模式也不只是针对于类型,某些运算符也可以作为模式的一部分。
本文简单认识一下 Patterns 的概念和在解构中的应用。另外,在流程控制中和匹配相关的有一个关键字 ---- switch 。下一篇将从 switch 语法的变化,继续了解 Patterns 的作用。谢谢观看 \~