Function 函数

在Dart语言中,函数也是对象,这点和其他语言有点不一样。

函数是一等对象

Dart 是完全面向对象语言,函数本身也是对象,类型为 Function

可赋值给变量、作为参数传给其他函数;

类实例可直接像函数一样调用

1 标准函数定义

// 完整带返回值、参数类型标注
bool isNoble(int atomicNumber) {
  return _nobleGases[atomicNumber] != null;
}
Code language: Dart (dart)

完整例子

// 稀有气体原子号映射表
final Map<int, String> _nobleGases = {
  2: 'He',
  10: 'Ne',
  18: 'Ar',
  36: 'Kr',
  54: 'Xe',
  86: 'Rn',
};

// 完整带返回值、参数类型标注
// int 不可空,不能传入 null,所以改为 int? 支持空参数
bool isNoble(int? atomicNumber) {
  return _nobleGases[atomicNumber] != null;
}

void main() {
  print(isNoble(null));       // null不在map键中 → false
  print(isNoble(2));          // 存在稀有气体 → true
  print(isNoble(9));          // 不存在 → false
  print(isNoble(86));         // 存在 → true
}
Code language: Dart (dart)
false
true
false
trueCode language: JavaScript (javascript)

2 省略类型

Effective Dart 编码规范建议,对外暴露的公共接口(公开 API)必须手动标注完整类型。

如果你是内部的函数,则可以省略类型

例如


final Map<int, String> _nobleGases = {
  2: 'He',
  10: 'Ne',
  18: 'Ar',
  36: 'Kr',
  54: 'Xe',
  86: 'Rn',
};


isNoble(atomicNumber) {
  return _nobleGases[atomicNumber] != null;
}

void main() {
  print(isNoble(null));       // null不在map键中 → false
  print(isNoble(2));          // 存在稀有气体 → true
  print(isNoble(9));          // 不存在 → false
  print(isNoble(86));         // 存在 → true
}

Code language: Dart (dart)

上面例子,isNoble函数,去掉了返回类似bool,参数也省略了int?类型。仍然可以正常使用。

当你不写类型时,Dart 会自动推断类型

  • 参数 atomicNumber:无显式类型 → 推导类型为 dynamic(动态类型,任意值都能传:数字、字符串、null 都行)
  • 返回值:无前置类型标注 → Dart 根据 return 右边表达式推断出返回 bool,只是代码不写出来
为什么公开 API 一定要写类型?

可读性:别人调用你的函数,一眼看懂入参、返回值是什么类型,不用读内部代码猜;

类型安全dynamic 动态类型关闭静态检查,传错类型不会编译报错,容易线上崩溃;

IDE 提示:标注类型后编辑器会自动补全、参数提示;无类型时 IDE 无法精准提示。

什么时候可以省略类型?

仅建议局部私有代码,函数只在当前文件内部使用、不对外被调用。

箭头简写语法(=>)

当你的函数里面的函数体({}中的代码),只有一句返回表达式的时候,可以使用简写成箭头函数。

例如

bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
Code language: Dart (dart)
  • => expr 等价于 { return expr; }
  • 箭头与分号之间只能写表达式,不能写语句(如 if,可用三元表达式替代)。
// 稀有气体原子序数映射表
final Map<int, String> _nobleGases = {
  2: '氦',
  10: '氖',
  18: '氩',
  36: '氪',
  54: '氙',
  86: '氡',
};

// 箭头简写函数:单行表达式直接返回,等价于带 return 的块函数
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;

void main() {
  // 测试存在的稀有气体
  print(isNoble(2));   // true
  print(isNoble(18));  // true

  // 测试普通元素
  print(isNoble(6));   // false(碳)
  print(isNoble(99));  // false(无对应元素)
}
Code language: Dart (dart)

Parameters 参数

函数支持必选位置参数,其后只能二选一:命名参数 / 可选位置参数,不能混用。

调用 / 定义参数时支持尾随逗号 ,,提升代码可读性。

1. 命名参数 Named parameters

{} 包裹,默认可选;加 required 强制传参。

1)基础可空命名参数(无默认值,默认是null,类型必须可空 ?

void enableFlags({bool? bold, bool? hidden}) {}
// 调用时显式指定参数名
enableFlags(bold: true, hidden: false);
Code language: Dart (dart)

完成例子

/// 开启文本样式标记:加粗、隐藏
void enableFlags({bool? bold, bool? hidden}) {
  print('加粗:$bold,隐藏:$hidden');
}

void main() {
  // 完整传所有命名参数
  enableFlags(bold: true, hidden: false);

  // 只传其中一个命名参数,另一个为 null
  enableFlags(bold: true);

  // 只传 hidden
  enableFlags(hidden: true);

  // 不传任何参数,两个值都是 null
  enableFlags();
}
Code language: Dart (dart)

执行结果

D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
加粗:true,隐藏:false
加粗:true,隐藏:null
加粗:null,隐藏:true
加粗:null,隐藏:nullCode language: JavaScript (javascript)

2)设置编译期常量默认值(无需可空),因为已经又默认值了,所以不为可空也可以

void enableFlags({bool bold = false, bool hidden = false}) {}
enableFlags(bold: true); // hidden 自动取 false

Code language: Dart (dart)

例子

/// 开启文本样式标记:加粗、隐藏
void enableFlags({bool bold = false, bool hidden = false}) {
  print('加粗:$bold,隐藏:$hidden');
}

void main() {
  enableFlags();//不传参数两个参数有默认值 上面设置都是false 加粗:false,隐藏:false
}
Code language: Dart (dart)

3)强制必传命名参数 required(注:Flutter 组件构造器经常用到) 如下例子是Flutter,读者可以到Flutter的课程中学习。

const Scrollbar({super.key, required Widget child});
// required 参数也允许可空类型
const Scrollbar({super.key, required Widget? child});

Code language: Dart (dart)

Dart 不强制命名参数必须放末尾,可穿插在参数列表任意位置

repeat(times: 2, () {})Code language: CSS (css)
2. 可选位置参数 Optional positional parameters

[] 包裹,不传时默认 null,可设置常量默认值。

// 无默认值,类型带 ?
String say(String from, String msg, [String? device]) {
  var result = '$from says $msg';
  if (device != null) result = '$result with a $device';
  return result;
}
assert(say('Bob', 'Howdy') == 'Bob says Howdy');
assert(say('Bob', 'Howdy', 'smoke signal') == 'Bob says Howdy with a smoke signal');

Code language: Dart (dart)

完整例子

// [String? device] 可选位置参数,无默认值,不传则为 null
String say(String from, String msg, [String? device]) {
  var result = '$from says $msg';
  if (device != null) {
    result = '$result with a $device'; //如果device不为null,则再信息的后面带上设备
  }
  return result;
}

void main() {
  // 不传第三个可选参数
  String res1 = say('Bob', 'Howdy');
  print(res1);

  // 传入第三个可选位置参数
  String res2 = say('Bob', 'Howdy', 'smoke signal');
  print(res2);

  // 手动传 null 给可选参数
  String res3 = say('Alice', 'Hi', null);
  print(res3);
}
Code language: Dart (dart)

设置默认值:

String say(String from, String msg, [String device = 'carrier pigeon']) {
  return '$from says $msg with a $device';
}
assert(say('Bob', 'Howdy') == 'Bob says Howdy with a carrier pigeon');

Code language: Dart (dart)

完整例子

// 可选位置参数,设置默认值 carrier pigeon(信鸽)
String say(String from, String msg, [String device = 'carrier pigeon']) {
  return '$from says $msg with a $device';
}

void main() {
  // 不传第三个可选参数,自动使用默认值 carrier pigeon
  String res1 = say('Bob', 'Howdy');
  print(res1); //Bob says Howdy with a carrier pigeon

  // 手动传入自定义设备,覆盖默认值
  String res2 = say('Bob', 'Howdy', 'smoke signal');
  print(res2); //Bob says Howdy with a smoke signal

  String res3 = say('Alice', 'Hello', 'mobile phone');
  print(res3); // Alice says Hello with a mobile phone
  
  String res4 = say('Jack', 'Tom', 'website foxdevelop.com');
  print(res4); // Jack says Tom with a page
}
Code language: Dart (dart)

执行结果

Built firstdart:firstdart.
Bob says Howdy with a carrier pigeon
Bob says Howdy with a smoke signal
Alice says Hello with a mobile phone
Jack says Tom with a website foxdevelop.comCode language: JavaScript (javascript)


main () 入口函数

所有 Dart 程序必须存在顶层 main(),是程序唯一入口。

  1. 无参基础版本
void main() {
  print('Hello, wellcome to foxdevelop.com!');
}

Code language: Dart (dart)
Hello, wellcome to foxdevelop.com!Code language: CSS (css)
  1. 命令行程序接收启动参数
// 执行命令:dart run firstdart.dart 1 test
void main(List<String> arguments) {
  print(arguments); // [1, test]
}

Code language: Dart (dart)

复杂命令行参数解析可使用官方 args 库。

上面的firstdart.dart 要改为你的dart文件名


函数作为一类对象 First-class objects

1. 函数作参数传递

void printElement(int element) => print(element);
var list = [1, 2, 3];
list.forEach(printElement); // 直接传入函数

Code language: Dart (dart)

完整例子

void main() {
    void printElement(int element) => print(element);
	var list = [1, 2, 3];
	list.forEach(printElement); // 直接传入函数
}
Code language: Dart (dart)

输出

1
2
3

2. 匿名函数赋值给变量

void main() {
    var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
	print(loudify('hello')); //!!! HELLO !!! 
}
Code language: Dart (dart)

函数类型 Function Type

可显式声明函数变量类型

格式:返回值 Function(形参列表, {命名参数})

void greet(String name, {String greeting = 'Hello'}) => print('$greeting $name!');
// 定义函数类型变量
void Function(String, {String greeting}) g = greet;
g('Dash', greeting: 'Howdy');

Code language: Dart (dart)

可通过 typedef 给函数类型起别名,复用、提升可读性。

{String greeting}) → 带一个命名参数 greeting,类型 String

void main() {
    void greet(String name, {String greeting = 'Hello'}) => print('$greeting $name!');
	// 定义函数类型变量
	void Function(String, {String greeting}) g = greet;
	g('Jack', greeting: 'Howdy');
}
Code language: Dart (dart)

输出

Howdy Jack!

匿名函数(Lambda / Closure 闭包)

无函数名的函数,两种写法:

  1. 代码块写法(多行)
void main() {
    const list = ['apples', 'bananas'];
	var uppercaseList = list.map((item) {
	  return item.toUpperCase();
	}).toList();
	print(uppercaseList);
}
Code language: Dart (dart)

上面代码中的 (item) { return item.toUpperCase(); } 就是匿名函数(lambda)

输出:[APPLES, BANANAS]

  1. 箭头简写(单行表达式)
void main() {
    const list = ['fox', 'develop','foxdevelop.com'];
    var uppercaseList = list.map((item) => item.toUpperCase()).toList();
    uppercaseList.forEach((item) => print('$item: ${item.length}'));
}

Code language: Dart (dart)

输出

FOX: 3
DEVELOP: 7
FOXDEVELOP.COM: 14Code language: HTTP (http)

词法作用域 Lexical scope

Dart 采用静态词法作用域:变量作用域由代码大括号层级决定,内层可访问外层所有变量。

bool topLevel = true;
void main() {
  var insideMain = true;
  void myFunction() {
    var insideFunction = true;
    void nestedFunction() {
      var insideNestedFunction = true;
      // 全部可访问
      assert(topLevel && insideMain && insideFunction && insideNestedFunction);
    }
  }
}

Code language: Dart (dart)

词法闭包 Lexical closures

闭包:函数对象可以捕获并持有外层作用域变量,脱离原作用域后仍能访问。

Function makeAdder(int addBy) {
  // 捕获外部变量 addBy
  return (int i) => addBy + i;
}

void main() {
  var add2 = makeAdder(2);
  var add4 = makeAdder(4);
  assert(add2(3) == 5);
  assert(add4(3) == 7);
}

Code language: Dart (dart)

下面对上面的执行过程进行逐步分析

第一步:var add2 = makeAdder(2);

  1. 执行 makeAdder,传入 addBy = 2
  2. 创建匿名函数:(int i) => 2 + i
  3. 将这个函数赋值给变量 add2,也就是这个时候add2其实是一个函数对象 它的函数体是(int i) => 2 + i 传入int i 返回i+2
  4. makeAdder 函数执行结束,但匿名函数牢牢记住 addBy=2

第二步:var add4 = makeAdder(4);

  1. 再次执行 makeAdder,传入 addBy = 4
  2. 创建全新独立的匿名函数:(int i) => 4 + i
  3. 赋值给 add4,它单独保存一份 addBy=4,和 add2 互不干扰,也就是addBy在两个函数对象add2和add4中,被记录到了函数体里面,一个里面的addBy是2 一个的addBy是4
  • makeAdder(2) 执行时,会生成一个独立闭包,单独绑定一份 addBy = 2,赋值给 add2
  • makeAdder(4) 执行时,生成另一个全新闭包,单独绑定一份 addBy = 4,赋值给 add4
  • add2add4 是两个完全互不干扰的函数对象,各自私有存储捕获的 addBy,互不影响。

第三步:调用 add2(3)

执行内部函数:2 + 3 = 5,断言成立

第四步:调用 add4(3)

执行内部函数:4 + 3 = 7,断言成立

函数截取 Tear-offs

调用函数 / 方法时不加括号,会生成 tear-off(轻量闭包),替代手写匿名 lambda,代码更简洁。

推荐写法,直接传入函数就可以。

var charCodes = [68, 97, 114, 116];
var buffer = StringBuffer();
charCodes.forEach(print); // 函数截取
charCodes.forEach(buffer.write); // 方法截取

Code language: Dart (dart)

冗余写法

charCodes.forEach((code) => print(code));
charCodes.forEach((code) => buffer.write(code));

Code language: Dart (dart)

函数相等性判断

  • 顶层函数、静态方法:函数名完全相等则 ==
  • 实例方法:只有绑定同一个对象实例的方法截取才相等。
void foo() {}
class A {
  static void bar() {}
  void baz() {}
}
void main() {
  var x = foo;
  assert(foo == x);

  x = A.bar;
  assert(A.bar == x);

  var v = A(), w = A(), y = w;
  x = w.baz;
  assert(y.baz == x); // 同一实例,相等
  assert(v.baz != w.baz); // 不同实例,不相等
}

Code language: Dart (dart)

返回值 Return values

  1. 所有函数必有返回值:无显式 return 时,隐式追加 return null;
foo() {}
assert(foo() == null);

Code language: Dart (dart)
  1. 返回多个值:使用 Record 记录元组
(String, int) foo() {
  return ('something', 42);
}

Code language: Dart (dart)

Getter & Setter 存取器

所有属性读取 / 赋值本质是调用 getter /setter;普通变量自动生成隐式存取器,也可手动显式定义。

优势:对外统一 .属性 调用,内部逻辑可随时修改,无需改动外部代码。

String _secret = 'Hello';

// Getter 读取
String get secret {
  print('调用getter');
  return _secret.toUpperCase();
}

// Setter 赋值
set secret(String newMsg) {
  print('调用setter');
  if (newMsg.isNotEmpty) _secret = newMsg;
}

void main() {
  print(secret); // 读取,触发getter → HELLO
  secret = 'Dart is fun'; // 赋值,触发setter
  print(secret); // DART IS FUN
}

Code language: Dart (dart)

* 生成器 Generators

惰性按需生成序列,分同步、异步两种,使用 yield 产出值。

  1. 同步生成器 sync*,返回 Iterable
Iterable<int> naturalsTo(int n) sync* {
  int k = 0;
  while (k < n) yield k++;
}

Code language: Dart (dart)
  1. 异步生成器 async*,返回 Stream
Stream<int> asyncNaturalsTo(int n) async* {
  int k = 0;
  while (k < n) yield k++;
}

Code language: Dart (dart)
  1. 递归生成器优化 yield*(转发另一个生成器,提升性能)
Iterable<int> naturalsDownFrom(int n) sync* {
  if (n > 0) {
    yield n;
    yield* naturalsDownFrom(n - 1);
  }
}

Code language: Dart (dart)

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注