说到到某个语言的语法可能大家会觉得很枯燥、乏味,而日常开发中我们往往更加注重的是业务逻辑和页面开发,语法的使用大多也停留在满足基本的需求。其实 Dart
语法有很多有意思的地方的,仔细探究一下你会发现,它的简洁清晰、灵活多样的语法会让人爱不释手。在本文中,我们将探索 Dart 语法的各种奇妙之处吧。
unwrap
操作
在 Flutter
中,unwrap
操作常常用于处理可能为空的数据,以便过滤掉空值并只保留非空值。其使用场景也相当广泛,例如 为 Future
和 Streams
添加 unwrap
来处理掉非空数据,或者从网络请求或其他异步操作中获取数据,并在数据流中处理结果等等,如下面这段代码:
1 | extension Unwrap<T> on Future<T?> { |
unwrap
函数将可能为空的 Future
解包,如果 Future
返回的值不为 null
,则将值包装在一个新的 Future
中返回,否则返回一个空的 Future
。调用示例:
1 | class ImagePickerHelper { |
这里用到图片选择器插件 image_picker
,只有当返回的 xFile
不为空时才进行后续操作。如果不调用 unwrap
函数,此时这里返回的 xFile
为 optional
类型,要使用之前需要判断是否为 null
。日常开发中这种情况还不少,给 Future
添加 Unwrap
函数之后这样非空判断集中在这一个函数里面处理。
unwrap
不仅在 Future
中使用,还可以为 Streams
添加 unwrap
操作,代码如下:
1 | extension Unwrap<T> on Stream<T?> { |
unwrap
方法,通过 where
过滤掉了 null
的事件,并使用 cast()
方法将结果转换为 Stream<T>
类型,将可空的事件转换为非空的事件流,下面是调用代码:
1 | void main() { |
通过 extension
给 Future
和 Streams
添加 unwrap
函数后让我们的代码看起来清晰简洁多了,有没有?
数组的展开、合并和过滤
下面代码为任意类型的可迭代对象(Iterable
)添加名为 Flatten
的扩展。在这个扩展中,函数 flatten
使用了递归算法将多层嵌套的 Iterable
里面的所有元素扁平化为单层 Iterable
。
1 | extension Flatten<T extends Object> on Iterable<T> { |
注意了上面代码中使用了 yield
关键字,在 Flutter
中,yield
关键字用于生成迭代器,通常与sync*
或 async*
一起使用。它允许您在处理某些数据时逐步生成数据,而不是在内存中一次性处理所有数据。对于处理大量数据或执行长时间运行的操作非常有用,因为它可以节省内存并提高性能。
这个和 ES6
中使用 function*
语法和 yield
关键字来生成值一个东西,也是逐个生成值,而不需要一次性生成所有值。以下是 JS
写法:
1 | function* generateNumbers(n) { |
我们来看看 Dart
中的 flatten()
函数的调用:
1 | Future<void> main() async { |
嵌套的集合可能在数据处理、转换或展示中经常遇到,而将这些嵌套的集合扁平化可以简化数据处理过程,使代码更加简洁和易于理解。另外一点,递归展多维数组在面试中经常会出现,说不定哪天就用上了哈。
如果将两个数组合并成一个数组该怎么操作呢?其实和 Map
的合并相似,也是用到了自定义操作符 operator
,来看看怎么实现的。
1 | extension InlineAdd<T> on Iterable<T> { |
添加了两个操作符:+
和 &
。将一个元素或者另一个可迭代对象添加到当前的可迭代对象中,然后返回一个新的可迭代对象,让可迭代对象 terable
有了合并数组的功能。
当数组中有一个为 null
的对象时,该如何过滤掉这个 null
对象呢,很简单可以这样做:
1 | extension CompactMap<T> on Iterable<T?> { |
Map
的过滤和合并
下面代码是 Map
类型的 extension
,为 Map
类型添加了查找过滤的函数。
1 | extension DetailedWhere<K, V> on Map<K, V> { |
where
: 接受一个函数作为参数,该函数接受Map
的键和值作为参数,并返回一个布尔值。whereKey
: 接受一个只接受键作为参数的函数。whereValue
: 这个方法接受一个只接受值作为参数的函数。
下面是调用:
1 | void main(){ |
其中 where
方法先使用 entries
获取 Map
的键值对列表,然后使用 entries.where
方法对列表中的每个键值对进行过滤,最后使用 fromEntries
方法将过滤后的键值对列表转换回 Map
,最后返回的新的 Map
中只包含满足条件的键值对,达到对 Map
中键值过滤的效果,也让代码更加简洁和易读。
Map
过滤还有另外一种写法
1 | extension Filter<K, V> on Map<K, V> { |
Map
其它一些更有趣的 extension
,如 Merge
功能,将两个 Map
合并成一个,代码如下:
1 | extension Merge<K, V> on Map<K, V> { |
上面的代码用到了 operator
关键字,在 Dart
中,operator
关键字用于定义自定义操作符或者重载现有的操作符。通过 operator
关键字,我们可以为自定义类定义各种操作符的行为,使得我们的类可以像内置类型一样使用操作符。
如 operator +
来定义两个对象相加的行为,operator []
来实现索引操作,operator ==
来定义相等性比较。这种语义式的也更加符合直觉、清晰易懂。
下面来看看 Map
的 Merge
功能调用代码例子:
1 | const userInfo = { |
调用的时候也很简单直接 userInfo | address;
,这种操作在处理数据更新或合并配置等情况下特别有用。使用的时候需要注意的是,如果两个 Map
中有重复的键,那么上述操作会保留第一个 Map
中的值。
小结
怎么样,上面的这些 Dart
的语法是不是很有意思,有没有函数式编程那味儿,后面还会单独一篇来分享 Dart
语言面向对象的设计。好了,今天就到这里,也希望通过本文的分享,能够激发大家对 Dart
语言的兴趣,感谢您的阅读,更多干货文章扫描下方的二维码关注我的公众号“Flutter技术实践”。