1. Dart 语言相关

1.Dart中的 ?? 和 ?. 分别是什么作用?

  • ??:空值合并运算符
    如果左边表达式不为 null,返回左边;否则返回右边。
String? name = null;
String display = name ?? "Guest"; // "Guest"

  • ?.:安全调用运算符
    当左边对象不为 null 时,调用其成员;否则返回 null(不会抛出异常)。
String? name = null;
int? length = name?.length; // null,不会报错

⚠️ 注意事项

?? 的右侧不一定是默认值,也可以是一个表达式或函数调用(会短路求值)。
?. 与 !(强制解包)谨慎搭配,避免在不明确是否为空时使用 !。

2. 简述Dart的异步模型,Future 和 Stream 的区别。

异步模型:Dart 是单线程异步模型,基于事件循环(Event Loop)。异步任务通过 Future 或 Stream 调度,不会阻塞主线程。

  • Future:表示一个一次性的异步计算结果(成功或失败)。可通过 then、catchError 或 async/await 处理。
  • Stream:表示一个零个或多个异步数据序列,随时间推送事件。可监听多个值、错误、完成事件。

⚠️ 注意事项:

  • Future 的 await 只等待单个结果;Stream 需要 await for 或监听器。
  • Stream 分为单订阅(single-subscription)和广播(broadcast)两种,注意区分。

3. mixin 和继承有什么区别?何时使用 mixin?

  • 继承(extends):类与类之间是“is-a”关系,子类获得父类所有非私有成员,且只能单继承。
  • mixin:通过 with 关键字实现代码复用,可“混入”多个 mixin 的方法和属性,没有层级限制。

使用场景:

  • 需要跨类层次共享通用行为(如日志、序列化、工具方法)。
  • 避免深层继承导致的复杂结构。

⚠️ 注意事项:

  • mixin 不能有构造函数(除非用 mixin class)。
  • on 关键字可以限制 mixin 只能用在特定类型的类上。
  • 当你需要“复用行为”而不是构建类型层次时,优先考虑 mixin;
    当类之间本质上是“父类-子类”关系时,使用 extends。

4. 说说Dart中的空安全(null safety)是如何保证安全的?

  • 非空类型(Type):变量默认不能为 null,编译器会强制检查初始化与赋值。
  • 可空类型(Type?):允许为 null,使用前必须进行空检查或使用 ?.、!。
  • 延迟初始化(late):允许非空变量稍后初始化,但若使用前未赋值会抛出异常。
  • 类型提升:对可空变量进行 if (variable != null) 判断后,作用域内自动提升为非空类型。

⚠️ 注意事项:

  • late 变量应谨慎使用,避免依赖外部保证。
  • 空安全在 Dart 2.12 及以上默认开启,迁移老代码需运行 dart migrate。

5. Dart 中 final 和 const 的区别是什么?

  • final:只能赋值一次,值在运行时确定。
  • const:编译时常量,值必须在编译时确定,且会隐式地 final。const 可以用于常量表达式,且相同值的 const 对象是单例的。

⚠️ 注意事项:

  • const 构造函数可以优化性能,因为编译时常量可以复用。
  • final 实例变量必须在构造体运行前初始化。

6 什么是 Dart 的 isolate?与线程有何不同?

  • isolate 是 Dart 的并发单元,独立内存空间,不共享状态,通过 SendPort / ReceivePort 传递消息。
  • 不同于操作系统线程(共享内存),isolate 没有竞态条件,但通信成本更高。

⚠️ 注意事项:

  • compute 函数是 Isolate 的简单封装,适合将耗时任务放到后台 isolate 执行。
  • 不要频繁创建 isolate。

7. async / await 与 Future.then 哪种更好?为什么?

async/await 更易读,错误处理可以用 try-catch,而 .then 需要单独捕获错误。两者本质相同,async/await 是语法糖。

⚠️ 注意事项:

  • 在循环中使用 await 会串行执行;如果需要并行,应使用 Future.wait。

8. 解释 Dart 中的 factory 构造函数。

factory 构造函数不总是创建新实例,可以返回现有对象(如从缓存获取)或子类对象。它没有 this 访问权限。

⚠️ 注意事项:

  • factory 命名构造函数常用于实现单例模式或对象池。

9. Dart 中 ??= 运算符的作用?

当变量为 null 时进行赋值。例如 a ??= 3; 如果 a 为 null 则赋值为 3,否则保持不变。

⚠️ 注意事项:

  • 相当于 a = a ?? 3;,但更简洁。
  • 只在左侧为 null 时计算右侧表达式。

10. 什么是 extension 方法?使用场景?

extension 允许为现有类添加新方法或属性,无需继承或修改原类。常用于扩展第三方库类或系统类(如 String)。

⚠️ 注意事项:

  • 扩展方法不会覆盖原类方法。
  • 要注意命名冲突,可通过 hide 或显式指定扩展名解决。

11. Dart 中的 typedef 有什么用途?

为函数类型定义一个别名,提高代码可读性。也可以定义泛型函数类型。Dart 2.13+ 支持 typedef 用于非函数类型(如 typedef IntList = List<int>;)。

⚠️ 注意事项:

  • 主要用于回调类型声明,使用时确保函数签名匹配。

12 解释 late 关键字及其风险。

late 表示一个非空变量可以延迟初始化,在第一次访问前必须赋值,否则抛出异常。常用于 late final 组合或依赖注入。

⚠️ 注意事项:

  • 过度使用 late 可能导致运行时错误,不如使用可空类型加安全调用。
  • 避免在 late 变量初始化时访问 this 未完全构建的对象。

12 Dart 中如何实现枚举的扩展(如添加方法)?

Dart 2.17+ 支持增强型枚举,可以包含字段、构造函数和方法。示例:

enum Status {
  loading, success, error;

  bool get isError => this == Status.error;
}

早期版本可以通过扩展方法模拟。

⚠️ 注意事项:

枚举的构造函数必须是 const。

13. sync* 和 async* 生成器的区别?

  • sync* 返回 Iterable,使用 yield 同步产生值。
  • async* 返回 Stream,使用 yield 异步产生值,可等待 Future。

⚠️ 注意事项:

  • async* 必须用 await for 或监听来消费。
  • 生成器函数不能执行 return 返回值(除非空返回)。
最后编辑于 :
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容