22 个 Flutter 开发者必知必会的 Dart 面试题(2020)
Dart 是一门面向对象语言,但是它采用基于类的编程。它只允许单一继承,语法风格接近C语言。随着 Flutter 广泛应用,Dart 作为 Flutter 的推荐语言慢慢的进入开发者视野。在 2018 - 2019 一年时间内,Dart 的使用率狂增 532%。2020 作为 Flutter 开发者的你准备好迎接新的挑战了吗?下面是整理了一些常见的 Dart 面试中可能会被问及的问题。
Q1: 什么是 Dart ?Flutter 为什么推荐使用它?
话题: Flutter
难度: ⭐⭐
Dart 是一个用于 Flutter App 开发的 面向对象, 垃圾回收 的编程语言。它由 Google 开源并维护,并且被应用在 Google 所有内部、外部的社区应用上。之所以 Flutter 将 Dart 作为首选语言,原因有以下几点:
- Dart 是 AOT ([Ahead Of Time])运行前编译,几乎所有的 Flutter 都可以用 Dart 编写。使用 AOT 语言的优点就是使 Flutter 具有更好的性能,而且几乎所有组件都可以自定义。
- Dart 也可以通过 JIT (Just In Time) 进行编译,以缩短开发周期,优化开发流程(典型的应用就是 Flutter 的热重载)。
- Dart 允许 Flutter 使用 JSX 或者 XML 之类的作为界面构建布局的声明语言,这使得程序更易读和理解。由于使用这种通用的语言作为布局的声明,使得 Flutter 可以很简单的就可以时间布局的替换与修改。
🔗 源码地址: hackernoon.com
Q2: 什么是 Fat 箭头函数? 在 Dart 中什么场景下会使用它?
话题: Flutter
难度: ⭐⭐
当你的方法只有一个表达式作为返回值的时候,你可以使用它的简化方式实现,一个形似 (){ return expression; }
的语句。
当你的代码只有单行的时候可以省略大括号。
在 (=>
) 和 (;
)之间只能包含一个表达书,不能出现语句。例如: if 不能用在此处,但是 conditional 表达式可以,下面是一个例子:
// 普通方法
void function1(int a) {
if (a == 3) {
print('arg was 3');
} else {
print('arg was not 3');
}
}
// 箭头函数
void function2(int a) => print('arg was ${a == 3 ? '' : 'not '}3');
🔗 Source: stackoverflow.com
Q3: Dart中的可选参数和必填参数是如何区分的?
Topic: Flutter
Difficulty: ⭐⭐⭐
必填参数
Dart 需要必填参数来完成函数或者方法的定义。
findVolume(int length, int breath, int height) {
print('length = $length, breath = $breath, height = $height');
}
findVolume(10,20,30);
可选参数
- 可选参数必须放在必填参数之后
- 在 Flutter/Dart 中, 有三种实现方式:
- 命名
- 通过
[]
定义 - 例如.
getUrl(int color, [int favNum])
- 通过
- 位置
- 通过 ‘{}’ 定义
- 例如.
getUrl(int color, {int favNum})
- 设置默认值
- 设置参数默认值
- 例如.
getUrl(int color, [int favNum = 6])
- 命名
🔗 Source: stackoverflow.com
Q4: 名称可选参数和位置可选参数的区别?
话题: Flutter
难度: ⭐⭐⭐
两者都属于可选参数:
位置可选参数:
通过
[ ]
包裹的是位置可选参数。例如:getHttpUrl(String server, String path, [int port=80]) { // ... }
你可以定义多个位置可选参数:
getHttpUrl(String server, String path, [int port=80, int numRetries=3]) { // ... }
上述代码中:
port
和numRetries
都是可选参数,并且具有默认值。你可以不传递第三个第四个参数,并成功调用getHttpUrl
。但是,你如果想要传递第四个参数,则第三个也不能省略,因为位置可选参数以位置作为判断依据。
名称可选参数:
通过
{ }
包裹的参数叫名称可选参数.getHttpUrl(String server, String path, {int port = 80}) { // ... }
同样,你也可以指定多个名称可选参数:
getHttpUrl(String server, String path, {int port = 80, int numRetries = 3}) { // ... }
名称可选参数不会受到位置约束,只要在调用
getHttpUrl
之后执行其名称即可。getHttpUrl('example.com', '/index.html', numRetries: 5, port: 8080); getHttpUrl('example.com', '/index.html', numRetries: 5);
🔗 Source: stackoverflow.com
Q5: 怎么理解 Flutter/Dart 中的 Stream?
话题: Flutter
难度: ⭐⭐⭐
Dart异步编程的两个特性
Future
和Stream
。stream 就是事件流或者管道,是一些的 ‘异步’ 事件。它会在上一个事件完成时通知你进行下一个事件。
Streams 无论用什么方式创建,都会以相同的方式返回并使用: asynchronous for loop( await for)。例子:
Future<int> sumStream(Stream<int> stream) async { var sum = 0; await for (var value in stream) { sum += value; } return sum; }
Streams 提供 asynchronous 序列化的数据。
该序列化数据包含了用户生成的时间和重文件中读取的数据。
你可以通过 await for 的
listen()
来处理 Stream API 返回的数据流。Streams 提供了错误相应的处理方法。
Streams 有两种方式: single subscription(订阅) 和 broadcast(广播)。
🔗 源码: dart.dev
Q6: 说明下 Streams 两种类型?
话题: Flutter
难度: ⭐⭐⭐
下面是两种 Streams 类型说明:
Single subscription streams(单一订阅)
- 最常见,最基本的 streams 实现方式。
- 它的数据大部分是 sequence of events。单一订阅必须以正确的顺序交付事件,并且中间不能有任何异常。
- 这是当你在读取文件或接受网络请求的时候产生的 stream 。
- 这样的流不具备幂等性,再次接收可能会不同于上次的请求。
- 当你开始监听,数据将被提取并以块的形式提供。
- Broadcast streams(广播)
- 适用于可以一次处理一个的单个消息。例如,这种流可用于浏览器中的鼠标事件。
- 您可以随时开始收听这样的流,并且在收听时会触发事件。
- 多个收听者可以同时收听,您可以在取消上一个订阅后稍后再次收听。
🔗 源码: dart.dev
Q7: 什么是空感知运算符 ?
话题: Flutter
难度: ⭐⭐⭐
Dart 提供了一些操作符,以方便处理可能为null的值。
这其中一个 ??= 赋值运算符,该运算符仅在变量值为空的时候才为其赋值,使用如下:
int a; // 定义了变量,并未赋值,所以这里变量值为 null a ??= 3; print(a); // <-- 打印 3. a ??= 5; print(a); // <-- 依然打印 3.
另一个空感知运算符
??
,在表达式值不为 null 的时候,它会返回左侧表达式的值,否则返回右侧表达式的值:print(1 ?? 3); // <-- 打印 1. print(null ?? 12); // <-- 打印 12.
🔗 来源: dart.dev
Q8: 怎么来监测判定一个异步 viod 方法已经执行完成呢?
话题: Flutter
难度: ⭐⭐⭐
修改返回类型为 Future<void>
.
Future<void> save(Folder folder) async {
.....
}
然后你可就可以使用 await save(...);
和 save().then(...);
来判断了。
🔗 Source: stackoverflow.com
Q9: 如何在 Dart 中将异步函数声明为变量?
话题: Flutter
难度: ⭐⭐⭐
在普通函数基础之上,dart 提供了一个Future 语法糖。只要变更该函数返回类型为一个 Future 就可以了:
class Example {
Future<void> Function() asyncFuncVar;
Future<void> asyncFunc() async => print('Do async stuff...');
Example() {
asyncFuncVar = asyncFunc;
asyncFuncVar().then((_) => print('Hello'));
}
}
void main() => Example();
🔗 源码: stackoverflow.com
Q10: 如何填充 Dart List?
话题: Flutter
难度: ⭐⭐⭐
代码:
final List<Ball> _ballList =[Ball(),Ball(),Ball(),Ball(),Ball(),]
怎样才能部多次执行 Ball()
,又能生成多个 Ball()
呢?
这里可以使用 collection-for
,他会生成 Ball()
的多个不同实例。
final List<Ball> _ballList = [
for (var i = 0; i < 5; i += 1) Ball(),
];
如果需要多个 Ball()
的相同实例,可以使用 List.filled
这种更简便的方法:
final List<Ball> _ballList = List<Ball>.filled(5, Ball());
🔗 源码: stackoverflow.com
Q11: whenCompleted() 与 then() 的异同?
话题: Flutter
难度: ⭐⭐⭐
.whenComplete
将在 Future 没有错误的情况下完成的时候触发,而.then
会返回一个新的 Future ,根据其结果然后通过onValue
(成功的返回) 或者onError
(失败,带有错误的返回) 来处理。.whenCompleted
是 “finally“。
🔗 源码: stackoverflow.com
Q12: 在 Flutter/Dart 中,如何获取两 list 的不同?
Topic: Flutter
Difficulty: ⭐⭐⭐
假定你有 list [1,2,3,4,5,6,7]
和 [3,5,6,7,9,10]
.那么该如何获取两者之间的不同呢?既 [1, 2, 4]
你可以这样做:
List<double> first = [1,2,3,4,5,6,7];
List<double> second = [3,5,6,7,9,10];
List<double> output = first.where((element) => !second.contains(element));
或者这样:
List<double> first = [1,2,3,4,5,6,7];
List<double> second = [3,5,6,7,9,10];
List<double> output = [];
first.forEach((element) {
if(!second.contains(element)){
output.add(element);
}
});
// output list 应该就是你想要的结果
无论哪种情况都需要遍历该 larger list.
🔗 源码: stackoverflow.com
Q13: 解释下 async, await 在 Flutter/Dart 中的应用?
话题: Flutter
难度: ⭐⭐⭐⭐
async(Asynchronous)允许你同时进行多项工作。下面是以下常见异步应用场景:
- 通过网络接口获取数据。
- 数据库写入操作。
- 从文件读取数据。
在 Dart 中的异步操作,你可以使用 Future
, async
和 await
关键字来实现。
通过 async
和 await
关键字声明一个异步函数,并等待其返回结果。在使用 async
和 await
的时候,请注意一下几点:
- 异步功能是基于
async
关键字,所以必须添加该关键字。 await
关键字仅在async
声明的函数中起作用。
在第一个 async
和 await
关键字之间的代码是立即执行的。
下面是个例子:
import 'dart:async';
class Employee {
int id;
String firstName;
String lastName;
Employee(this.id, this.firstName, this.lastName);
}
void main() async {
print("getting employee...");
var x = await getEmployee(33);
print("Got back ${x.firstName} ${x.lastName} with id# ${x.id}");
}
Future<Employee> getEmployee(int id) async {
//通过颜值两秒来模拟服务器请求时间延迟
await Future<Employee>.delayed(const Duration(seconds: 2));
//构建一个员工信息,并返回
var e = new Employee(id, "Joe", "Coder");
return e;
}
🔗 Source: dart.dev
Q14: Flutter/Dart 中 Future 如何使用?
话题: Flutter
难度: ⭐⭐⭐⭐
A Future 用于表示可能会出现的错误或误差。 * Future * 可以注册通过回调处理,当相应的错误被抛出的时候你就可以捕获并处理它了。例如:(译者注:作为一个phper,从php角度出发,这就是try catch吧)
Future<int> future = getFuture(); future.then((value) => handleValue(value)) .catchError((error) => handleError(error));
如果异常不在预期之内,将会抛出类型为
Future<void>
的异常。Future * 是异步操作,并且存在两种状态:
- 第一种状态 Uncompleted。该状态表示当您调用 asynchronous 函数时,它返回的是一个未完成的 Future。Future 正在等待函数的 asynchronous 操作完成或将错误抛出。
- *Completed 如果 asynchronous 操作成功,则将来以一个值完成。否则,它会以错误完成。
🔗 源码: api.dart.dev
Q15: Dart\Flutter 中 Future 和 Stream 的异同?
话题: Flutter
难度: ⭐⭐⭐⭐
相同点:
Future
和Stream
都属于异步操作。- 两者都有一定的潜在价值。
不同点:
Stream
is a combination of Futures.Future
有且只有一个响应值,而Stream
可能会有多个响应值。
🔗 源码: medium.com
Q16: Dart AOT 是如何工作的?
话题: Flutter
难度: ⭐⭐⭐⭐
- Dart源代码将被翻译成汇编文件,然后汇编器会将汇编文件编译成用于不同体系结构的二进制代码。
- 对于移动应用程序,源代码针对多个处理器ARM,ARM64,x64以及两个平台(Android和iOS)进行编译。这意味着每种支持的处理器和平台组合都有多个结果二进制文件。
🔗 源码: flutterbyexample.com
Q17: 操作符 ?? 和 ? 的异同。
话题: Flutter
难度: ⭐⭐⭐⭐
??
- 这是一个 空赋值操作符,当左侧表达式为 null 的时候才会返回右侧的表达式,否则返回其左侧的表达式:
print(1 ?? 3); // <-- 打印 1.
print(null ?? 12); // <-- 打印 12.
?.
这是个条件属性访问符,用于访问可能为空的对象或者属性:
单个表达式中可以使用多个
?.
;myObject?.someProperty?.someMethod()
如果
myObject
或myObject.someProperty
为 null,则上述代码返回 null (并且永远不会调用someMethod()
)。
🔗 源码: flutter.dev
Q18: Dart 中 async 和 async* 的异同?
话题: Flutter
难度: ⭐⭐⭐⭐
async
返回一个Future
而async*
将返回Stream
async
关键词可应用于某些执行时间较长的方法。他会以Future
的形式返回。- 如果您希望返回多个值您可以用
async*
,他将返回结果以Stream
的形式返回 。 async*
使用返回Stream
并提供了一些可以以yield
关键字返回值的语法糖。
🔗 源码: stackoverflow.com
Q19: Dart 中如何比较两个不同的日期?
话题: Flutter
难度: ⭐⭐⭐⭐
您可以通过将另一个日期转换为utc
并将其与isAtSameMomentAs
方法进行比较来实现。下面是一个 Demo:
void main(){
String dateTime = '2020-02-03T08:30:00.000Z';
int year = 2020;
int month = 2;
int day = 3;
int hour = 8;
int minute = 30;
var dt = DateTime.utc(year,month,day,hour,minute);
print(DateTime.parse(dateTime).isAtSameMomentAs(dt));
}
🔗 源码: stackoverflow.com
Q20: Dart “non-nullable by default” 是什么意思?
话题: Flutter
难度: ⭐⭐⭐⭐
- Non-nullable by default 意味着 Dart 中声明的的任何变量不能为
null
。 - 不可以在没赋值之前访问该变量。
- 另外,变量不能被赋值为
null
。
具体看例子:
void main() {
String word;
print(word); // 报错
word = 'Hello, ';
print(word); // 合法
}
void main() {
String word;
word = null; // 进制
world = 'World!'; // 允许
}
🔗 源码: stackoverflow.com
Q21: 在 Dart/Flutter 中 ._() 方法是什么意思?
话题: Flutter
难度: ⭐⭐⭐⭐
- 在 Dart 中,以下划线开头的函数表明该函数是私有的。
- 形如
Class._();
的函数可能是一个构造函数 (在 Flutter 中,也可能是某个对象的副本的构造函数,如:AnotherClass.copy(...);
). - 形如
Class._();
不是必须的,除非你想要在构造函数中执行某些操作。
🔗 Source: stackoverflow.com
Q22: Dart 中如何将 List 转换为 Map?
话题: Flutter
难度: ⭐⭐⭐⭐
你可以使用 Map.fromIterable:
var result = Map.fromIterable(l, key: (v) => v[0], value: (v) => v[1]);
或者 collection-for (从 Dart 2.3 开始支持此方法):
var result = { for (var v in l) v[0]: v[1] };
🔗 源码: stackoverflow.com
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
好文! 用了那么久,不总结,真的容易忘,而且也表达不了那么多的知识点