Day 06 - 函数基础
目标:掌握 ArkTS 函数的定义方式、参数机制和基本用法
预计时间:1.5-2 小时
前置知识:Day 01-05(变量与类型、运算符、条件控制、循环、数组)
第一部分:函数定义
1.1 函数声明
基本语法:
function 函数名(参数1: 类型, 参数2: 类型): 返回值类型 {
// 函数体
return 返回值;
}
无返回值函数(void):
function turnOnDevice(deviceId: number): void {
console.log(`设备 ${deviceId} 已开启`);
// 不需要 return 语句
}
function logError(message: string): void {
console.log(`错误: ${message}`);
return; // 可以提前退出,但不返回具体值
}
有返回值函数:
function getDeviceStatus(deviceId: number): string {
return `设备 ${deviceId} 运行中`;
}
function calculatePower(voltage: number, current: number): number {
return voltage * current;
}
function isValidDeviceId(id: number): boolean {
return id > 0 && id < 10000;
}
使用示例:
// 调用函数
let status: string = getDeviceStatus(1001);
console.log(status);
let power: number = calculatePower(220, 0.5);
console.log(`功率: ${power}W`);
let isValid: boolean = isValidDeviceId(1001);
console.log(`设备ID是否有效: ${isValid}`);
C/C++ 对比:
| 特性 | C/C++ | ArkTS |
|---|---|---|
| 无返回值 | void func() |
function func(): void |
| 返回整数 | int func() |
function func(): number |
| 返回字符串 | std::string func() |
function func(): string |
| 返回布尔 | bool func() |
function func(): boolean |
小提示:ArkTS 中所有类型都需要显式声明,不能像 C 那样省略返回类型。
ArkTS 与 C/C++ 的重要差异:
- 声明与实现不分离:ArkTS 函数声明和实现必须在一起,不支持 C/C++ 那种头文件声明、源文件实现的分离模式。
// C++ 可以分离声明和实现
int add(int a, int b); // 声明
int add(int a, int b) { return a + b; } // 实现
// ArkTS 声明和实现必须在一起
function add(a: number, b: number): number {
return a + b;
}
- 参数名不可省略:C/C++ 函数声明可以省略参数名,但 ArkTS 必须写参数名。
// C++ 声明时可省略参数名
int add(int, int);
// ArkTS 必须写参数名
function add(a: number, b: number): number;
// interface 中的函数签名也必须写参数名
interface Calculator {
(a: number, b: number): number; // 必须有参数名
}
1.3 箭头函数
箭头函数是 ArkTS 中定义函数的简洁方式,语法更短,适合作为回调函数或简单函数使用。
基本语法:
(参数1: 类型, 参数2: 类型): 返回值类型 => {
// 函数体
return 返回值;
}
完整写法:
let add = (a: number, b: number): number => {
return a + b;
};
let result: number = add(3, 5);
console.log(`结果: ${result}`);
简写形式(单行表达式):
当函数体只有一行表达式时,可以省略 {} 和 return:
let multiply = (a: number, b: number): number => a * b;
let square = (x: number): number => x * x;
console.log(multiply(4, 7)); // 28
console.log(square(5)); // 25
单参数括号不可省略:
在 TypeScript 中,单参数无类型注解时可省略括号。但在 ArkTS 中,由于强制要求显式类型声明,单参数也必须保留括号:
// TypeScript 中可省略括号(无类型注解)
let double = x => x * 2; // TS 允许
// ArkTS 必须有类型注解,因此括号不可省略
let double = (x: number): number => x * 2; // ArkTS 正确
let double = x: number => x * 2; // 语法错误
无参数箭头函数:
let getTimestamp = (): number => Date.now();
let getRandom = (): number => Math.random();
console.log(`当前时间戳: ${getTimestamp()}`);
C++ Lambda 对比:
// C++ Lambda
auto add = [](int a, int b) -> int { return a + b; };
auto multiply = [](int a, int b) { return a * b; }; // 返回类型推断
auto square = [](int x) { return x * x; };
// ArkTS 箭头函数
let add = (a: number, b: number): number => { return a + b; };
let multiply = (a: number, b: number): number => a * b;
let square = (x: number): number => x * x;
相似之处:
- 都是匿名函数的简洁写法
- 都可以捕获外部变量(闭包)
- 都支持参数类型和返回类型声明
不同之处:
- C++ Lambda 使用
[]捕获列表,ArkTS 自动捕获(闭包) - ArkTS 单参数必须保留括号(因为需要类型声明),C++ 不能省略
闭包简要说明:
C++ Lambda 需要显式声明捕获哪些外部变量,而 ArkTS 箭头函数自动捕获外层作用域的变量:
// C++ Lambda 显式捕获
int x = 10;
auto f = [x]() { return x * 2; }; // 值捕获 x
// ArkTS 自动捕获
let x: number = 10;
let f = (): number => x * 2; // 自动捕获 x
C/C++ 对比:箭头函数 ≈ C++ Lambda,都是匿名函数的语法糖,适合作为回调或简短函数使用。闭包的详细讲解将在后续课程中进行。
第二部分:参数机制
2.1 必需参数
必需参数是函数调用时必须提供的参数,数量和类型都必须匹配。
function setDeviceName(deviceId: number, name: string): void {
console.log(`设备 ${deviceId} 名称设置为: ${name}`);
}
// 正确调用
setDeviceName(1001, "客厅灯");
// 错误调用(编译报错)
// setDeviceName(1001); // ❌ 缺少参数 name
// setDeviceName("客厅灯", 1001); // ❌ 参数类型不匹配
// setDeviceName(1001, "客厅灯", 1); // ❌ 参数数量过多
参数类型检查:
function calculateArea(width: number, height: number): number {
return width * height;
}
let area1: number = calculateArea(10, 20); // ✅ OK
// let area2: number = calculateArea("10", 20); // ❌ 类型错误
// let area3: number = calculateArea(10); // ❌ 参数不足
C/C++ 对比:
- C/C++ 编译时会检查参数数量和类型
- ArkTS 在编译阶段进行更严格的类型检查
- 两者都不允许参数数量不匹配的情况
2.2 可选参数
用 ? 标记的参数是可选的,调用时可以不提供。不提供时值为 undefined。可选参数必须在参数列表的末尾。
基本语法:
function configureDevice(
deviceId: number,
name: string,
brightness?: number // 可选参数
): void {
console.log(`配置设备 ${deviceId}: ${name}`);
if (brightness !== undefined) {
console.log(`亮度: ${brightness}`);
}
}
// 调用方式
configureDevice(1001, "卧室灯"); // ✅ 不提供可选参数
configureDevice(1002, "客厅灯", 80); // ✅ 提供可选参数
可选参数的位置限制:
// ❌ 错误:可选参数不能在必需参数前面
// function wrongFunc(optional?: number, required: string): void {}
// ✅ 正确:可选参数必须在末尾
function correctFunc(required: string, optional?: number): void {}
使用前的判断:
function sendCommand(
deviceId: number,
command: string,
timeout?: number
): boolean {
let actualTimeout: number;
if (timeout !== undefined) {
actualTimeout = timeout;
} else {
actualTimeout = 5000; // 默认值
}
console.log(`发送命令到设备 ${deviceId}: ${command}`);
console.log(`超时时间: ${actualTimeout}ms`);
return true;
}
sendCommand(1001, "turnOn"); // 使用默认超时 5000ms
sendCommand(1001, "turnOn", 3000); // 使用指定超时 3000ms
C/C++ 对比:
- C++ 使用默认参数
int timeout = 5000 - C++17 可以使用
std::optional<int> - ArkTS 使用
?标记,不传时为undefined
小提示:读取可选参数前必须判断是否为
undefined,否则可能引发运行时错误。
2.3 默认参数值
给参数设置默认值,调用时如果不提供该参数,就使用默认值。
基本语法:
function createDevice(
deviceId: number,
name: string,
brightness: number = 100, // 默认值为 100
timeout: number = 5000 // 默认值为 5000ms
): void {
console.log(`创建设备: ${deviceId} - ${name}`);
console.log(`默认亮度: ${brightness}`);
console.log(`超时时间: ${timeout}ms`);
}
// 调用方式
createDevice(1001, "智能灯"); // brightness=100, timeout=5000
createDevice(1002, "台灯", 80); // brightness=80, timeout=5000
createDevice(1003, "夜灯", 50, 3000); // brightness=50, timeout=3000
默认参数的位置说明:
语法上,默认参数后面可以有必需参数,但调用时必须提供所有参数,默认参数退化为必需参数,默认值基本失去意义:
// 语法允许,但 b 的默认值用不上
function test(a: number, b: number = 10, c: number): void {
console.log(`a=${a}, b=${b}, c=${c}`);
}
test(1, 2, 3); // a=1, b=2, c=3(必须提供所有参数)
test(1, undefined, 3); // a=1, b=10, c=3(显式传 undefined 才能用默认值)
实践建议:默认参数应放在末尾,才能发挥默认值的作用。
// ✅ 推荐:默认参数放在最后
function correctFunc(a: number, c: number, b: number = 10): void {}
可选参数 vs 默认参数:
| 特性 | 可选参数 ? |
默认参数 = value |
|---|---|---|
| 不提供时的值 | undefined |
默认值 |
| 参数类型 | number | undefined |
number |
| 使用场景 | 参数可能不存在 | 参数有常用值 |
| 使用前判断 | 需要判断 undefined |
直接使用 |
| 类型安全 | 需要额外处理 | 类型确定 |
可选参数与默认参数不可互换:
// 可选参数:类型包含 undefined
function func1(x?: number): void {
// x 类型: number | undefined
}
// 默认参数赋值 undefined:类型错误
function func2(x: number = undefined): void {
// ❌ Type 'undefined' is not assignable to type 'number'
}
// 可选参数示例
function func1(x?: number): void {
if (x !== undefined) {
console.log(`x = ${x}`);
} else {
console.log("x 未提供");
}
}
// 默认参数示例
function func2(x: number = 10): void {
console.log(`x = ${x}`); // 直接使用,无需判断
}
func1(); // 输出: x 未提供
func2(); // 输出: x = 10
C/C++ 对比:
- C++ 默认参数:
int func(int a, int b = 10) - ArkTS 默认参数:
function func(a: number, b: number = 10) - 两者都要求默认参数在参数列表末尾
C/C++ 对比:ArkTS 的默认参数与 C++ 的默认参数概念完全一致,都是为参数提供默认值。
2.4 剩余参数
剩余参数(Rest Parameters)用于接收不定数量的参数,语法为 ...args: Type[]。
剩余参数本质是数组,...表示数组展开。
基本语法:
function sumNumbers(...numbers: number[]): number {
let sum: number = 0;
for (let i: number = 0; i < numbers.length; i++) {
sum += numbers[i];
}
return sum;
}
// 调用方式
console.log(`sum: ${sumNumbers()}`); // 0
console.log(`sum: ${sumNumbers(1)}`); // 1
console.log(`sum: ${sumNumbers(1, 2, 3)}`); // 6
console.log(`sum: ${sumNumbers(1, 2, 3, 4, 5)}`); // 15
剩余参数与固定参数混合使用:
function sendCommand(
deviceId: number,
command: string,
...params: string[]
): void {
console.log(`设备 ${deviceId} 命令: ${command}`);
if (params.length > 0) {
console.log(`参数: ${params.join(", ")}`);
}
}
sendCommand(1001, "turnOn");
sendCommand(1001, "setBrightness", "80");
sendCommand(1001, "setColor", "255", "128", "0");
实际应用示例:
// 计算多个设备的平均温度
function averageTemperature(...temps: number[]): number {
if (temps.length === 0) {
return 0;
}
let sum: number = 0;
for (let i: number = 0; i < temps.length; i++) {
sum += temps[i];
}
return sum / temps.length;
}
console.log(`平均温度: ${averageTemperature(25, 30, 28)}`); // 27.666...
console.log(`平均温度: ${averageTemperature(20, 22)}`); // 21
C/C++ 对比:
- C 语言使用
va_list实现可变参数 - C++11 使用可变参数模板
template<typename... Args> - ArkTS 使用
...args: Type[],更简洁,本质是数组
// C++ 可变参数模板
template<typename... Args>
int sum(Args... args) {
return (args + ...); // C++17 折叠表达式
}
// ArkTS 剩余参数
function sum(...numbers: number[]): number {
let total: number = 0;
for (let i: number = 0; i < numbers.length; i++) {
total += numbers[i];
}
return total;
}
剩余参数的类型一致性:
...args: Type[] 表示所有剩余参数组成一个数组,数组元素类型必须统一:
// ✅ 正确:所有剩余参数都是 number
function sum(...numbers: number[]): number { ... }
// ❌ 错误:不能混合类型
sum(1, "hello", 3); // 类型错误
通过联合类型接收不同类型参数:
// 使用联合类型
function mixed(...args: (string | number)[]): void {
// args 可以包含 string 或 number
}
mixed(1, "hello", 2, "world"); // ✅ OK
C/C++ 对比:ArkTS 的剩余参数比 C/C++ 的可变参数更简单易用,本质是一个数组,可以直接使用数组的方法。
第三部分:小结与练习
知识点对比总结表
| 概念 | 语法 | 说明 | C/C++ 对应 |
|---|---|---|---|
| 函数声明 | function foo(): void {} |
标准函数定义 | void foo() |
| 箭头函数 | () => {} |
简洁语法 | Lambda [](){} |
| 无返回值 | : void |
不返回任何值 | void |
| 必需参数 | x: number |
必须提供 | 普通参数 |
| 可选参数 | x?: number |
可不提供,值为 undefined |
std::optional |
| 默认参数 | x: number = 10 |
不提供时使用默认值 | 默认参数 = 10 |
| 剩余参数 | ...args: number[] |
接收不定数量参数 | va_list / 可变模板 |
ArkTS 语法规范提醒
-
console.log 只接受 string 参数:
// ❌ 错误 console.log(123); // ✅ 正确 console.log(`数值: ${123}`); console.log(String(123)); -
显式类型声明:
// ✅ 正确 function add(a: number, b: number): number { return a + b; } let x: number = 10; -
使用 === 而非 ==:
if (x === 10) { } // ✅ 正确 if (x == 10) { } // ❌ 避免使用 -
语句末尾加分号:
let x: number = 10; // ✅
练习题
练习 1:函数声明与箭头函数(选择题)
题目:以下哪个是 ArkTS 中正确的箭头函数写法?
A. let add = function(a: number, b: number): number => { return a + b; };
B. let add = (a: number, b: number): number => { return a + b; };
C. let add = (a: number, b: number) => number { return a + b; };
D. let add = (a: number, b: number): number -> { return a + b; };
答案
B解析:
- A 错误:
function关键字不能与箭头=>混用 - B 正确:标准箭头函数语法
- C 错误:箭头函数使用
=>而非=> number - D 错误:ArkTS 使用
=>而非->
练习 2:可选参数(代码题)
题目:编写函数 configureSensor,参数如下:
sensorId: number(必需)name: string(必需)interval?: number(可选,采样间隔)
函数内部打印传感器配置信息。如果 interval 未提供,则不打印采样间隔。
参考答案
function configureSensor(
sensorId: number,
name: string,
interval?: number
): void {
console.log(`传感器 ${sensorId}: ${name}`);
if (interval !== undefined) {
console.log(`采样间隔: ${interval}ms`);
}
}
// 测试
configureSensor(1, "温度传感器"); // 不打印间隔
configureSensor(2, "湿度传感器", 1000); // 打印间隔
练习 3:默认参数(代码题)
题目:编写函数 createLED,参数如下:
deviceId: number(必需)name: string(必需)brightness: number = 100(默认参数,亮度 0-100)isOn: boolean = false(默认参数,开关状态)
函数打印 LED 的配置信息。
参考答案
function createLED(
deviceId: number,
name: string,
brightness: number = 100,
isOn: boolean = false
): void {
console.log(`LED设备: ${deviceId} - ${name}`);
console.log(`亮度: ${brightness}`);
console.log(`状态: ${isOn ? "开启" : "关闭"}`);
}
// 测试
createLED(1001, "客厅灯"); // brightness=100, isOn=false
createLED(1002, "卧室灯", 80); // brightness=80, isOn=false
createLED(1003, "书房灯", 60, true); // brightness=60, isOn=true
练习 4:剩余参数(代码题)
题目:编写函数 findMax,接收任意数量的数字参数,返回其中的最大值。如果没有参数,返回 0。
参考答案
function findMax(...numbers: number[]): number {
if (numbers.length === 0) {
return 0;
}
let max: number = numbers[0];
for (let i: number = 1; i < numbers.length; i++) {
if (numbers[i] > max) {
max = numbers[i];
}
}
return max;
}
// 测试
console.log(`最大值: ${findMax()}`); // 0
console.log(`最大值: ${findMax(10)}`); // 10
console.log(`最大值: ${findMax(10, 50, 30, 20)}`); // 50
console.log(`最大值: ${findMax(-5, -10, -3)}`); // -3
练习 5:综合应用(代码题)
题目:编写函数 logDeviceData,参数如下:
deviceId: number(必需)timestamp: number(必需,时间戳)level: string = "INFO"(默认参数,日志级别)...data: string[](剩余参数,数据项)
函数按格式打印设备日志信息。
参考答案
function logDeviceData(
deviceId: number,
timestamp: number,
level: string = "INFO",
...data: string[]
): void {
console.log(`[${level}] 设备${deviceId} @ ${timestamp}`);
for (let i: number = 0; i < data.length; i++) {
console.log(` - ${data[i]}`);
}
}
// 测试
logDeviceData(1001, 1699123456);
logDeviceData(1001, 1699123456, "WARN");
logDeviceData(1001, 1699123456, "ERROR", "温度过高", "风扇故障");
练习 6:参数类型判断(选择题)
题目:以下哪个函数声明是正确的?
A. function test(a?: number, b: string): void {}
B. function test(a: number = 10, b: string): void {}
C. function test(a: number, b?: string, c: number = 5): void {}
D. function test(a: number, b: string = "x", c?: number): void {}
参考答案
B、D解析:
- A 错误:可选参数
a?不能在必需参数b前面 - B 正确:语法上允许默认参数后面有必需参数,但调用时必须提供所有参数,默认值基本用不上
- C 错误:可选参数
b?后面不能有带默认值的参数c - D 正确:必需参数 → 默认参数 → 可选参数,顺序正确
参数顺序实践建议:必需参数 → 默认参数 → 可选参数
练习 7:箭头函数简写(代码题)
题目:将以下函数声明改写为箭头函数的简写形式(单行省略 {} 和 return):
function calculateCircleArea(radius: number): number {
return 3.14159 * radius * radius;
}
参考答案
let calculateCircleArea = (radius: number): number => 3.14159 * radius * radius;
练习 8:函数选择(选择题)
题目:以下场景分别适合使用哪种参数类型?
- 一个日志函数,日志级别通常是 "INFO",但用户可以指定其他级别
- 一个搜索函数,用户可以提供一个或多个关键词
- 一个设备配置函数,用户可以指定设备名称(必须)和描述(可选)
A. 默认参数、剩余参数、可选参数
B. 可选参数、默认参数、必需参数
C. 默认参数、可选参数、剩余参数
D. 剩余参数、默认参数、可选参数
参考答案
A
解析:
- 日志级别有常用值 → 默认参数
- 关键词数量不确定 → 剩余参数
- 描述可能不提供 → 可选参数
参考对比表
| 概念 | C/C++ | ArkTS |
|---|---|---|
| 函数声明 | int add(int a, int b) |
function add(a: number, b: number): number |
| 无返回值 | void func() |
function func(): void |
| 默认参数 | int timeout = 5000 |
timeout: number = 5000 |
| 可变参数 | template<typename... Args> / va_list |
...args: Type[] |
| Lambda | [](int x) { return x * 2; } |
(x: number) => x * 2 |

浙公网安备 33010602011771号