文章

Dart函数与闭包深度解析

从函数作为一等公民到词法闭包的底层实现,全面拆解Dart函数与闭包的语法特性与运行时原理

Dart函数与闭包深度解析

一句话概括

Dart的函数是”一等公民”——可以赋值给变量、作为参数传递、作为返回值返回;闭包则通过捕获词法作用域中的变量,实现了函数与其定义环境的绑定。

背景与意义

为什么理解函数与闭包对Flutter开发至关重要?

Flutter的整个UI系统构建在”函数组合”之上——Widget的build方法本质上是一个将数据转换为界面的函数,而State的管理大量依赖闭包和回调。

以一个典型的Flutter页面为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 一个普通的Flutter页面中,函数和闭包无处不在:
ListView.builder(
  itemCount: items.length,
  itemBuilder: (context, index) {
    // 👆 这是一个匿名函数 / 闭包
    return ListTile(
      title: Text(items[index].title),
      onTap: () {
        // 👆 这是一个回调闭包
        setState(() {
          // 👆 这也是一个闭包
          items[index].isSelected = !items[index].isSelected;
        });
      },
    );
  },
);

正是这种函数式风格让Flutter的UI代码兼具声明式的可读性和灵活的可扩展性。如果对函数和闭包理解不透彻,写出bug会非常隐蔽。

函数式编程在Dart中的定位

1
2
3
4
5
6
7
8
纯面向对象层 ← → 函数式特性层
    │               │
  class            ⭐ 函数是一等公民
  extends          ⭐ 匿名函数 / 箭头函数
  mixin            ⭐ 闭包捕获
  implements       ⭐ 高阶函数
    │               │
  Dart = OOP ⨁ 函数式

Dart不像Haskell那样纯函数式,也不像Java那样纯面向对象。它采取务实的多范式设计,在OOP骨架之上加上了函数式特性。

概念与定义

函数的核心概念

概念定义Dart示例
函数声明使用 返回类型 函数名(参数) 语法int add(int a, int b) => a + b;
匿名函数没有名字的函数字面量(int x) => x * 2
高阶函数接收函数作为参数或返回函数List.map(fn)Stream.listen(fn)
闭包捕获了外部作用域的匿名函数makeAdder(int n) => (int x) => x + n
函数类型返回值类型 Function(参数类型)int Function(int, int)
TEAR-OFF将方法引用作为函数对象list.forEach(print)——print就是tear-off

函数的声明方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 方式一:命名函数(最常用)
int square(int x) {
  return x * x;
}

// 方式二:箭头语法(单表达式函数体)
int squareArrow(int x) => x * x;

// 方式三:匿名函数赋值给变量
var squareVar = (int x) => x * x;

// 方式四:typedef定义函数类型
typedef IntTransformer = int Function(int);
IntTransformer squareTypedef = (int x) => x * x;

// 方式五:方法tear-off → 将实例方法作为函数
class Calculator {
  int multiply(int a, int b) => a * b;
}
void main() {
  final calc = Calculator();
  final op = calc.multiply; // tear-off: 将方法绑定到实例
  print(op(3, 4)); // 12
}

最小示例:函数与闭包基础演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
// functions_and_closures.dart
import 'dart:math';

void main() {
  print('=== 1. 匿名函数基础 ===');
  // 匿名函数直接使用
  [1, 2, 3, 4, 5].forEach((element) {
    print('元素: $element');
  });

  // 箭头函数版
  [1, 2, 3, 4, 5].forEach((e) => print('箭头: $e'));

  print('\n=== 2. 函数作为参数(高阶函数) ===');
  // 自定义高阶函数
  List<int> applyOperation(List<int> inputs, int Function(int) op) {
    return inputs.map(op).toList();
  }

  final doubled = applyOperation([1, 2, 3], (x) => x * 2);
  print('翻倍: $doubled');

  final squared = applyOperation([1, 2, 3], (x) => x * x);
  print('平方: $squared');

  print('\n=== 3. 闭包——捕获外部变量 ===');
  // 最简单的闭包
  int base = 10;
  var addBase = (int x) => x + base; // 捕获了base
  print('addBase(5) = ${addBase(5)}'); // 15

  // 修改捕获的变量
  base = 100;
  print('base修改后 addBase(5) = ${addBase(5)}'); // 105 — 闭包引用,而非拷贝

  print('\n=== 4. 函数工厂——返回闭包 ===');
  // 创建一个"乘数工厂"
  Function makeMultiplier(int factor) {
    return (int x) => x * factor; // 捕获factor
  }

  final double2 = makeMultiplier(2);
  final triple = makeMultiplier(3);

  print('double2(7) = ${double2(7)}');  // 14
  print('triple(7) = ${triple(7)}');    // 21
  // double2和triple虽然是同一个工厂创建的,但各自捕获了不同的factor值

  print('\n=== 5. 闭包陷阱演示 ===');
  // 经典的"循环中创建闭包"陷阱
  final callbacks = <Function()>[];

  for (var i = 0; i < 3; i++) {
    callbacks.add(() => print('闭包中的i: $i'));
  }

  print('执行var声明的闭包:');
  callbacks.forEach((cb) => cb());
  // 输出: 0, 1, 2 — 因为var i是循环作用域的局部变量,每次迭代都重新绑定

  // 对比:使用int?声明的类似JS行为
  final callbacks2 = <Function()>[];
  int? j;
  for (j = 0; j < 3; j++) {
    callbacks2.add(() => print('闭包中的j: $j'));
  }

  print('\n执行外部分配变量的闭包:');
  callbacks2.forEach((cb) => cb());
  // 输出: 3, 3, 3 — 因为所有闭包共享同一个j
}

// ===== 实用闭包示例 =====
// 计数器闭包
Function makeCounter() {
  int _count = 0; // 私有状态,被闭包捕获
  return () => ++_count;
}

// 缓存函数(memoization)—— 闭包保存缓存状态
int Function(int) memoize(int Function(int) fn) {
  final cache = <int, int>{};
  return (int x) {
    if (cache.containsKey(x)) {
      print('[缓存命中] $x -> ${cache[x]}');
      return cache[x]!;
    }
    print('[计算] $x');
    final result = fn(x);
    cache[x] = result;
    return result;
  };
}

核心知识点拆解

1. 函数是一等公民(First-Class Functions)

“函数是一等公民”意味着函数与其他值拥有同等地位

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
void main() {
  // 1. 赋值给变量
  final myFunction = (String name) => 'Hello, $name!';
  print(myFunction('Dart'));

  // 2. 作为参数传递
  void useFunction(String Function(String) greeter) {
    print(greeter('World'));
  }
  useFunction(myFunction);

  // 3. 作为返回值
  String Function(String) greetingFactory(String lang) {
    switch (lang) {
      case 'en': return (name) => 'Hello, $name!';
      case 'zh': return (name) => '你好,$name!';
      case 'ja': return (name) => 'こんにちは、$name!';
      default:   return (name) => 'Hi, $name!';
    }
  }
  final enGreet = greetingFactory('en');
  final zhGreet = greetingFactory('zh');
  print(enGreet('Alice'));
  print(zhGreet('张三'));

  // 4. 存储在集合中
  final operations = <int Function(int)>[
    (x) => x + 1,
    (x) => x * 2,
    (x) => x * x,
  ];
  print(operations.map((op) => op(5))); // (6, 10, 25)
}

2. 词法作用域与闭包捕获机制

词法作用域(Lexical Scoping)指的是变量的可见性由代码的书写位置决定,而非调用位置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
int global = 100; // 全局作用域

void outerFunction() {
  int outer = 10; // outer函数的作用域

  void innerFunction() {
    int inner = 1; // inner函数的作用域
    // inner可以访问 global, outer, inner
    print(global + outer + inner); // 111
  }

  innerFunction();
  // 这里不能访问 inner,inner是innerFunction的内部作用域
}

// 闭包捕获的"引用"语义
void main() {
  var closures = <int Function()>[];

  // 情况1: var声明在循环体内(每次循环独立绑定)
  for (var i = 0; i < 3; i++) {
    closures.add(() => i); // 捕获的是"每次循环的i"
  }

  // 情况2: var声明在循环体外(所有闭包共享绑定)
  int j;
  for (j = 0; j < 3; j++) {
    // 所有闭包捕获的是同一个j
  }
  // closures2里的每个闭包都看到j最后的值为3
}

闭包的底层捕获规则

1
2
3
4
捕获规则:
├── 值类型(int, double, bool) → 捕获对变量的"引用"而非值拷贝
├── 引用类型(对象) → 捕获对象引用
└── final变量 → 编译器可以优化为值拷贝(因为不可变)

3. 箭头函数 vs 块函数体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 箭头函数:单表达式,自动返回
int add(int a, int b) => a + b;

// 块函数体:多语句,需要return
int addWithBlock(int a, int b) {
  final sum = a + b;
  print('计算结果: $sum');
  return sum;
}

// 使用场景选择
void main() {
  // ✅ 简单变换 → 箭头
  final squares = [1, 2, 3].map((e) => e * e);

  // ✅ 多步操作 → 块体
  final processed = [1, 2, 3].map((e) {
    final doubled = e * 2;
    final squared = doubled * doubled;
    return squared;
  });

  // ⚠️ 注意:箭头函数不能写语句,只能写表达式
  // ❌   (x) => print(x); return x;   ← 不合法
  // ✅   (x) { print(x); return x; }  ← 正确
}

4. 可选参数与命名参数

Dart的函数参数系统非常丰富,这在闭包上下文中有特殊意义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 位置可选参数(用 [] 包裹)
String greet(String name, [String? greeting, String? punctuation]) {
  final g = greeting ?? 'Hello';
  final p = punctuation ?? '!';
  return '$g, $name$p';
}

// 命名可选参数(用 {} 包裹)
String greetNamed(String name, {String greeting = 'Hello', String punctuation = '!'}) {
  return '$greeting, $name$punctuation';
}

// 在闭包和高阶函数中使用可选参数
void main() {
  // 带命名参数的闭包
  final greeter = (String name, {String greeting = 'Hi'}) {
    return '$greeting, $name!';
  };
  print(greeter('Alice'));           // Hi, Alice!
  print(greeter('Bob', greeting: 'Hola')); // Hola, Bob!

  // 高阶函数传参
  [1, 2, 3].map((e) {
    // 这里可以使用块体加更多逻辑
    return e * 2;
  });
}

5. 函数类型语法

Dart的函数类型语法有新旧两种写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 旧语法(Dart 1.x 风格)- 仍兼容
typedef int OldTransformer(int value);

// 新语法(Dart 2.x+ 推荐)
typedef NewTransformer = int Function(int value);
// 或直接用:
int Function(int) // 匿名函数类型

// 复杂函数类型的表示
void main() {
  // 接受两个int返回一个int
  int Function(int, int) binaryOp = (a, b) => a + b;

  // 接受一个String和一个闭包,返回void
  void Function(String, void Function(int)) processor = (msg, callback) {
    callback(msg.length);
  };

  // 嵌套函数类型:接收int返回(接收int返回int) ← 柯里化的模拟
  int Function(int) Function(int) curriedAdder = (int a) {
    return (int b) => a + b;
  };
  final add5 = curriedAdder(5);
  print(add5(3)); // 8
}

实战案例:使用闭包实现Flutter状态管理中间件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// store_middleware.dart - 用函数和闭包实现的简易状态管理
import 'dart:collection';

// 定义Action
class Action {
  final String type;
  final dynamic payload;
  Action(this.type, {this.payload});

  @override
  String toString() => 'Action($type)';
}

// 定义状态
class AppState {
  final int counter;
  final List<String> logs;

  AppState({this.counter = 0, this.logs = const []});

  AppState copyWith({int? counter, List<String>? logs}) {
    return AppState(
      counter: counter ?? this.counter,
      logs: logs ?? this.logs,
    );
  }
}

// Reducer函数类型
typedef Reducer = AppState Function(AppState state, Action action);

// Middleware函数类型:接收store的dispatch和getState,返回一个处理函数
typedef Middleware = void Function(
  void Function(Action) dispatch,
  AppState Function() getState,
);

// Store——闭包管理状态
class Store {
  final Reducer _reducer;
  AppState _state;
  final List<Middleware> _middlewares;
  final List<void Function(AppState)> _listeners = [];

  // Dispatch函数(闭包管理)
  late final void Function(Action) dispatch;

  Store({
    required Reducer reducer,
    AppState? initialState,
    List<Middleware> middlewares = const [],
  })  : _reducer = reducer,
        _state = initialState ?? AppState(),
        _middlewares = middlewares {
    // 构建中间件链(洋葱模型)
    // 最内层是原始的_reducer调用
    void rawDispatch(Action action) {
      if (action == _lastDispatched) return; // 防止循环
      _lastDispatched = action;
      _state = _reducer(_state, action);
      _notifyListeners();
    }

    // 反向组装中间件链——每个中间件包裹下一层
    void Function(Action) next = rawDispatch;
    for (final middleware in _middlewares.reversed) {
      final currentNext = next; // 闭包捕获当前层
      next = (Action action) {
        middleware(currentNext, () => _state);
        // 注意:这里的middleware会调用currentNext(action)来继续传递
      };
      // 让中间件有机会在dispatch上"挂钩"
      // 实际上middleware接受dispatch和getState的时机
      // 是我们主动调用的,这里简化了Redux的compose逻辑
    }

    dispatch = (Action action) {
      // 经过中间件链处理
      _middlewares.isEmpty
          ? rawDispatch(action)
          : _applyMiddlewares(action);
    };
  }

  void _applyMiddlewares(Action action) {
    // 简化实现:让每个中间件获取dispatch和getState
    for (final middleware in _middlewares) {
      middleware(dispatch, () => _state);
    }
    // 实际中间件应在适当时候调用dispatch
    rawDispatch(action);
  }

  void rawDispatch(Action action) {
    if (action == _lastDispatched) return;
    _lastDispatched = action;
    _state = _reducer(_state, action);
    _notifyListeners();
  }

  AppState? _lastDispatched;

  AppState get state => _state;

  void subscribe(void Function(AppState) listener) {
    _listeners.add(listener);
  }

  void _notifyListeners() {
    for (final listener in _listeners) {
      listener(_state);
    }
  }
}

// 一个日志中间件(闭包的典型应用)
Middleware loggerMiddleware = (dispatch, getState) {
  // 这个闭包捕获了外部作用域的dispatch和getState
  // 但实际的"hook"是在dispatch时
  print('[Middleware] Logger 已注册');
  // 注意:真正的Redux中间件是返回一个函数
  // 这里为简化演示,我们采用不同模式
};

// 使用示例
void main() {
  // 创建Store
  final store = Store(
    reducer: (state, action) {
      switch (action.type) {
        case 'INCREMENT':
          return state.copyWith(counter: state.counter + 1);
        case 'DECREMENT':
          return state.copyWith(counter: state.counter - 1);
        case 'ADD_LOG':
          final logs = [...state.logs, action.payload as String];
          return state.copyWith(logs: logs);
        default:
          return state;
      }
    },
    initialState: AppState(counter: 0, logs: ['初始化']),
    middlewares: [
      (dispatch, getState) {
        print('中间件: 当前状态counter=${getState().counter}');
      },
    ],
  );

  // 订阅状态变化
  store.subscribe((state) {
    print('状态更新: counter=${state.counter}, logs=${state.logs.length}条');
  });

  // 分发Action
  store.dispatch(Action('INCREMENT'));
  store.dispatch(Action('INCREMENT'));
  store.dispatch(Action('DECREMENT'));
  store.dispatch(Action('ADD_LOG', payload: '用户登录'));

  print('\n最终状态: counter=${store.state.counter}');
}

底层原理(源码分析)

闭包的运行时表示

Dart的闭包在运行时是由编译器生成的闭包对象(Closure Object)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 源代码
int Function() makeCounter() {
  int count = 0;
  return () => ++count;
}

// Dart编译器生成的伪代码(类似)
class _makeCounter_closure implements int Function() {
  // 捕获的变量成为闭包对象的字段
  int count;

  _makeCounter_closure(this.count);

  @override
  int call() {
    return ++this.count;
  }
}

// main中:
void main() {
  final closure = _makeCounter_closure(0);
  print(closure()); // 1 → 调用closure.call()
  print(closure()); // 2
}

闭包对象的内存布局:

1
2
3
4
5
6
7
8
9
10
闭包对象在堆中的结构:
┌───────────────────────┐
│ [Class Header]         │ ← 指向闭包类的类型信息
├───────────────────────┤
│ capturedVariables[]    │ ← 捕获的变量数组(指向堆中变量)
│   ├── count: int      │    如果捕获的是基本类型,值直接嵌入
│   └── ...              │    如果捕获的是对象,存储对象引用
├───────────────────────┤
│ parentScopeChain[]     │ ← 父级作用域链(嵌套闭包时使用)
└───────────────────────┘

Tear-off的编译优化

方法tear-off是Dart的重要优化手段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Greeter {
  final String prefix;
  Greeter(this.prefix);

  String greet(String name) => '$prefix $name';
}

void main() {
  final g = Greeter('Hello');

  // 每次访问 x.greet 都会创建一个新的闭包对象
  final f1 = g.greet;
  final f2 = g.greet;
  print(f1 == f2); // false(实例方法tear-off每次创建新闭包)

  // 但静态方法tear-off是共享的
  // 静态方法tear-off每次返回相同的闭包
}

Dart VM中的函数调用

Dart VM使用”虚方法表”(vtable)实现函数调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Dart VM中调用函数的流程
void main() {
  int Function(int, int) op = (a, b) => a + b;
  // 1. op变量存储指向闭包对象的引用
  // 2. 调用 op(3, 4) 时,VM通过闭包对象的call()方法
  //    查找vtable中的_entry,然后执行
  print(op(3, 4));
}

// 对dynamic类型的函数调用
dynamic op2 = (int a, int b) => a + b;
print(op2(3, 4));
// dynamic调用会走间接寻址路径(更慢):
// 1. 运行时检查op2是否为Function类型
// 2. 反射查找call方法
// 3. 调用

高频面试题解析

问题1:Dart中闭包捕获变量时是”值捕获”还是”引用捕获”?如何证明?

解析

Dart闭包捕获变量是“引用捕获”(捕获变量的内存地址,而非值拷贝)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void main() {
  // 证明实验
  var value = 10;
  final closure = () => value;

  print(closure()); // 10

  // 修改外部变量
  value = 42;
  print(closure()); // 42 ← 如果是值捕获,这里应该是10
  // 闭包读取时从变量的内存地址读取,所以看到新值

  // 进一步的证明:多个闭包共享一个变量
  var shared = 0;
  final closures = [
    () => shared += 1,
    () => shared += 10,
    () => shared * 2,
  ];

  closures[0](); // shared = 1
  closures[1](); // shared = 11
  print(closures[2]()); // 22 ← 看到的是修改后的shared值
}

与Swift的对比

  • Dart:闭包默认引用捕获,可以修改捕获的变量
  • Swift:闭包默认值捕获,需要显式 __block 声明才可修改

问题2:Dart的forEach中用了async闭包,为什么不会按预期顺序执行?如何解决?

解析

1
2
3
4
5
6
7
8
9
// 问题代码
Future<void> processItems(List<int> items) async {
  items.forEach((item) async {
    await Future.delayed(Duration(seconds: item));
    print('处理完成: $item');
  });
  print('所有处理已完成');
}
// 输出: "所有处理已完成" 先打印,然后才逐个打印"处理完成"

原因forEach 的回调不等待async函数的Future完成。items.forEach((item) async {...}) 启动了一个个Fire-and-forget的异步任务,但forEach本身同步返回。

解决方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 方案一:使用 for 循环(推荐)
Future<void> processItemsFixed(List<int> items) async {
  for (final item in items) {
    await Future.delayed(Duration(seconds: item));
    print('处理完成: $item');
  }
  print('所有处理已完成');
}

// 方案二:使用 Future.forEach
Future<void> processItemsFuture(List<int> items) async {
  await Future.forEach(items, (int item) async {
    await Future.delayed(Duration(seconds: item));
    print('处理完成: $item');
  });
  print('所有处理已完成');
}

// 方案三:使用 Stream
Future<void> processItemsStream(List<int> items) async {
  await Stream.fromIterable(items)
    .asyncMap((item) async {
      await Future.delayed(Duration(seconds: item));
      print('处理完成: $item');
    })
    .forEach((_) {});  // 消费Stream使操作执行
  print('所有处理已完成');
}

问题3:Dart中箭头函数和普通函数的性能有差异吗?

解析

编译后没有实质性差异。箭头函数 => 只是语法糖,编译器会将其编译为与块函数相同的中间表示。

1
2
3
4
5
6
7
// 这两段代码编译后生成的Dart VM指令完全相同
int add1(int a, int b) => a + b;
int add2(int a, int b) { return a + b; }

// Dart编译器对两者的处理无差异
// 箭头函数在处理单表达式时反而减少了{}和return的解析开销
// 但这是编译期的差异,不影响运行效率

选择建议纯粹看可读性

1
2
3
4
5
6
7
8
// 单表达式 → 箭头函数更简洁
final doubled = list.map((e) => e * 2);

// 多步骤 → 块函数体更清楚
final processed = list.map((e) {
  final temp = complexTransformation(e);
  return validateAndClean(temp);
});

总结与扩展

核心要点

  1. 一等公民:函数可以赋值、传参、返回、存储,这在Flutter的Widget构建中无处不在
  2. 引用捕获:闭包捕获的是变量的引用而非值,这点与JS一致,与Swift不同
  3. 作用域链:Dart使用词法作用域,闭包可以访问定义时所有外层作用域的变量
  4. 闭包对象:编译后闭包被实现为带有capturedVariables字段的Callable对象
  5. tear-off:实例方法tear-off每次创建新闭包,静态方法tear-off是共享的

扩展思考

  • Dart 3.0 函数式特性增强:Records(记录类型)允许函数返回多个类型的值,Patterns(模式匹配)让参数解构更加优雅
  • 闭包与内存泄漏:在Flutter中,StatefulWidget的回调闭包如果持有了BuildContext引用,可能导致已销毁Widget的内存泄漏
  • Stream与闭包:Stream的listen回调如果捕获了外部Widget引用,需要注意取消订阅避免泄漏
  • Isolate中的闭包:Dart的Isolate之间不共享内存,传递给Isolate.spawn的闭包会被序列化,不能捕获可变的共享状态
本文由作者按照 CC BY 4.0 进行授权