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++ 的重要差异:

  1. 声明与实现不分离: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;
}
  1. 参数名不可省略: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 语法规范提醒

  1. console.log 只接受 string 参数

    // ❌ 错误
    console.log(123);
    
    // ✅ 正确
    console.log(`数值: ${123}`);
    console.log(String(123));
    
  2. 显式类型声明

    // ✅ 正确
    function add(a: number, b: number): number { return a + b; }
    let x: number = 10;
    
  3. 使用 === 而非 ==

    if (x === 10) { }  // ✅ 正确
    if (x == 10) { }   // ❌ 避免使用
    
  4. 语句末尾加分号

    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:函数选择(选择题)

题目:以下场景分别适合使用哪种参数类型?

  1. 一个日志函数,日志级别通常是 "INFO",但用户可以指定其他级别
  2. 一个搜索函数,用户可以提供一个或多个关键词
  3. 一个设备配置函数,用户可以指定设备名称(必须)和描述(可选)

A. 默认参数、剩余参数、可选参数

B. 可选参数、默认参数、必需参数

C. 默认参数、可选参数、剩余参数

D. 剩余参数、默认参数、可选参数

参考答案

A

解析

  1. 日志级别有常用值 → 默认参数
  2. 关键词数量不确定 → 剩余参数
  3. 描述可能不提供 → 可选参数

参考对比表

概念 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
posted @ 2026-04-09 11:39  thammer  阅读(2)  评论(0)    收藏  举报