零基础鸿蒙应用开发第十三节:函数的定义与使用

零基础鸿蒙应用开发学习计划表

前面已经多次用到函数,但从未系统全面讲解。函数是ArkTS代码组织的核心单元,也是实现逻辑封装、复用、解耦的基础工具。本节会帮你系统掌握ArkTS中函数的标准定义规范、参数处理规则、箭头函数简化写法,建立“规范编码、场景化用函数”的编程思维。

【学习目标】

  1. 理解函数封装、复用、解耦的核心价值,掌握传统函数与箭头函数的标准定义语法
  2. 熟练处理必需参数、默认参数、可选参数的语法规则,实现函数的灵活调用
  3. 理解函数重载的底层逻辑,适配多参数类型/个数的业务场景
  4. 掌握ArkTS函数类型约束核心规则,规避隐式any、匿名函数等语法错误
  5. 区分传统函数与箭头函数的适用场景,熟练运用箭头函数的简化写法

【学习重点】

  • 函数基础:传统函数/箭头函数的定义规范调用逻辑
  • 参数规则:必需→默认→可选的参数顺序及编译期类型校验
  • 函数重载:传统函数专属的多场景适配能力及优先级规则
  • 箭头函数:简化语法核心规则(单参数/单语句/无参数)
  • ArkTS约束:禁用普通匿名函数、普通函数嵌套,参数/返回值必须显式声明类型

一、工程结构

本节我们将创建名为FunctionBasicDemo的工程,基于 鸿蒙5.0(API12) 开发,使用 DevEco Studio 6.0+ 工具,项目结构目录如下:

FunctionBasicDemo/
├── entry/
│   ├── src/
│   │   ├── main/
│   │   │   ├── ets/
│   │   │   │   ├── entryability/  # 应用入口(默认生成,无需修改)
│   │   │   │   ├── pages/         # 页面代码(含UI+逻辑调用)
│   │   │   │   │   └── Index.ets  # 核心页面(函数调用测试)
│   │   │   │   └── utils/         # 工具函数目录
│   │   │   │       └── FunctionBasicTest.ets # 函数核心代码
│   │   │   ├── resources/         # 资源文件(默认生成,无需修改)
│   │   │   └── module.json5       # 模块配置(默认生成,无需修改)

二、函数的核心价值:封装与复用

函数是“输入→处理→输出”逻辑的可复用代码单元,核心价值体现在三个维度:

  • 复用性:一次定义多次调用,消除重复代码
  • 解耦性:拆分复杂逻辑为独立函数,便于维护
  • 安全性:强类型约束提前拦截类型错误

2.1 未封装的冗余写法

// 未封装:重复的计算与打印逻辑
console.log(`10 + 20 = ${10 + 20}`);
console.log(`30 + 40 = ${30 + 40}`);

2.2 封装为函数实现复用

// 封装为函数:一次定义,多次调用,强类型约束
function add(a: number, b: number): number {
  return a + b;
}
console.log(`add(10, 20) = ${add(10, 20)}`); // 输出:30
console.log(`add(30, 40) = ${add(30, 40)}`); // 输出:70

三、函数的基础定义与调用

3.1 传统函数模板

// 传统函数标准模板
function 函数名(参数1: 类型1, 参数2: 类型2): 返回值类型 {
  // 函数体逻辑
  return 运算结果; // 无返回值时无需return,且必须标注void
}

核心规范

  • 命名:小驼峰+简单动词(如multiply/printInfo),禁用func1/doSomething等模糊命名
  • 类型:参数必须显式声明类型,无返回值标注void
  • 限制:禁用普通匿名函数(function (){}),编译阶段直接报错

传统函数定义示例

// 示例1:有返回值 - 计算两数乘积
function multiply(a: number, b: number): number {
  return a * b;
}
const res = multiply(5, 6);
console.log(`5×6的结果:${res}`); // 输出:30

// 错误:参数未声明类型(隐式any,编译报错)
// function multiplyErr(a, b): number {
//   return a * b;
// }

// 示例2:无返回值 - 打印基础信息(必须标注void)
function printInfo(name: string, age: number): void {
  console.log(`姓名:${name},年龄:${age}`);
}
printInfo("小明", 18); // 输出:姓名:小明,年龄:18

// 不推荐:无返回值未标注void
// function printInfoErr(name: string, age: number) {
//   console.log(`姓名:${name},年龄:${age}`);
// }

3.2 箭头函数模板

箭头函数是传统函数的简化写法,适用于短逻辑场景,核心语法:

// 箭头函数标准模板
const 函数名 = (参数1: 类型1, 参数2: 类型2): 返回值类型 => {
  return 运算结果;
};

箭头函数定义示例

// 示例1:有返回值 - 两数求差
const subtract = (a: number, b: number): number => {
  return a - b;
};
console.log(`subtract(20, 8) = ${subtract(20, 8)}`); // 输出:12

// 示例2:无返回值 - 打印问候语
const printGreet = (name: string): void => {
  console.log(`你好,${name}!`);
};
printGreet("小红"); // 输出:你好,小红!

// 错误1:普通匿名函数(ArkTS明确禁用)
// const subtractErr = function(a: number, b: number): number {
//   return a - b;
// };

// 错误2:参数未声明类型(隐式any,编译报错)
// const printGreetErr = (name) => {
//   console.log(`你好,${name}!`);
// };

3.3 函数调用核心注意事项

  1. 调用时参数的个数、类型必须与声明完全匹配,否则编译报错
  2. 有返回值函数可接收结果(const res = add(1,2))或直接调用(add(1,2)
  3. 无返回值函数仅支持直接执行(printInfo("小明", 18)
  4. 所有参数/返回值必须显式声明类型,禁止隐式any类型

四、函数参数的灵活处理

4.1 必需参数

默认情况下参数为必需参数,调用时必须传入且类型匹配:

function printUser(name: string, age: number): void {
  console.log(`姓名:${name},年龄:${age}`);
}
printUser("小李", 20); // 正常调用,输出:姓名:小李,年龄:20

// 错误1:缺少参数(编译报错)
// printUser("小李");
// 错误2:类型不匹配(编译报错)
// printUser(20, "小李");

4.2 可选参数

参数名?标记,必须放在必需参数之后

function printUserDetail(name: string, age: number, gender?: string): void {
  if (gender) {
    console.log(`姓名:${name},年龄:${age},性别:${gender}`);
  } else {
    console.log(`姓名:${name},年龄:${age}`);
  }
}
printUserDetail("小王", 22); // 不传可选参数,输出:姓名:小王,年龄:22
printUserDetail("小张", 23, "男"); // 传入可选参数,输出:姓名:小张,年龄:23,性别:男

// 错误:可选参数前置(编译报错)
// function printUserDetailErr(name: string, gender?: string, age: number): void {
//   console.log(`姓名:${name},年龄:${age}`);
// }

4.3 默认参数

为参数指定默认值,建议放在“必需参数后、可选参数前”:

function printAddress(name: string, city: string = "北京"): void {
  console.log(`${name}的城市:${city}`);
}
printAddress("小赵"); // 使用默认值,输出:小赵的城市:北京
printAddress("小钱", "上海"); // 覆盖默认值,输出:小钱的城市:上海

// 不推荐:默认参数放在可选参数后(逻辑混乱,易出错)
// function printAddressErr(name: string, gender?: string, city: string = "北京"): void {
//   console.log(`${name}的城市:${city}`);
// }

4.4 参数类型校验

ArkTS通过静态类型校验规避运行时错误,参数类型不匹配会直接编译报错:

function getScoreLevel(score: number): string {
  if (score >= 90) return "优秀";
  if (score >= 80) return "良好";
  return "及格";
}
console.log(`getScoreLevel(85) = ${getScoreLevel(85)}`); // 输出:良好

// 错误:参数类型不匹配(编译报错)
// console.log(`getScoreLevel("85") = ${getScoreLevel("85")}`);

4.5 函数重载

仅支持传统函数,为同一函数名定义多套参数/返回值规则,编译期匹配最精确的签名:

// 步骤1:定义重载签名(按“精确→宽泛”排序)
function sum(a: number): number;
function sum(a: number, b: number): number;
function sum(a: number, b: number, c: number): number;

// 步骤2:实现重载(覆盖所有签名的逻辑)
function sum(a: number, b?: number, c?: number): number {
  return a + (b ?? 0) + (c ?? 0); // ?? 空值合并运算符,避免undefined参与计算
}

// 调用测试
console.log(`sum(1) = ${sum(1)}`); // 输出:1
console.log(`sum(1, 2) = ${sum(1, 2)}`); // 输出:3
console.log(`sum(1, 2, 3) = ${sum(1, 2, 3)}`); // 输出:6

// 类型适配重载示例
function format(val: number): string;
function format(val: string): string;
function format(val: number | string): string {
  return typeof val === "number" ? val.toFixed(2) : val.toUpperCase();
}
console.log(`format(3.1415) = ${format(3.1415)}`); // 输出:3.14
console.log(`format("hello") = ${format("hello")}`); // 输出:HELLO

// 常见错误
// 错误1:箭头函数尝试重载(无效果,编译不报错但逻辑失效)
// const sumErr = (a: number): number => a;
// const sumErr = (a: number, b: number): number => a + b;

// 错误2:重载签名顺序错误(宽泛在前,精确签名失效)
// function formatErr(val: number | string): string;
// function formatErr(val: number): string;

五、箭头函数的简化语法

核心简化规则

  1. 单语句函数体:省略{}return
  2. 单参数:必须显式声明类型,且建议保留括号(ArkTS推荐写法)
  3. 无参数:保留空括号()
  4. 禁止隐式any,所有参数必须标注类型
// 1. 单语句简化(两数求和)
const addSimple = (a: number, b: number): number => a + b;
console.log(`addSimple(3, 4) = ${addSimple(3, 4)}`); // 输出:7

// 2. 单参数简化(推荐:保留括号+显式类型)
const doubleNum = (num: number): number => num * 2;
console.log(`doubleNum(6) = ${doubleNum(6)}`); // 输出:12

// 3. 无参数简化(获取当前时间戳)
const getTimeStamp = (): number => new Date().getTime();
console.log(`当前时间戳:${getTimeStamp()}`);

// 4. 字符串拼接简化
const getFullName = (firstName: string, lastName: string): string => `${lastName}${firstName}`;
console.log(`getFullName("明", "张") = ${getFullName("明", "张")}`); // 输出:张明

// 常见错误(均编译报错)
// const doubleNumErr1 = num: number => num * 2; // 省略参数括号
// const doubleNumErr2 = num => num * 2; // 无类型注解(隐式any)
// const getFullNameErr = (firstName, lastName) => `${lastName}${firstName}`; // 隐式any

六、箭头函数的适用场景

// 1. 短逻辑封装(替代多行传统函数)
const square = (num: number): number => num * num;
console.log(`square(7) = ${square(7)}`); // 输出:49

// 2. 回调函数(定时器/数组遍历)
setTimeout(() => console.log(`1秒后执行`), 1000);
[1, 2, 3].forEach((item) => console.log(`数组元素:${item}`));

// 3. 高阶函数参数(接收函数作为入参)
type CalcFunc = (a: number, b: number) => number;
function runCalc(func: CalcFunc, a: number, b: number): number {
  return func(a, b);
}
const divRes = runCalc((a, b) => a / b, 10, 2);
console.log(`10/2的结果:${divRes}`); // 输出:5

// 错误:回调使用普通匿名函数(ArkTS禁用)
// setTimeout(function() { console.log(`1秒后执行`); }, 1000);

七、ArkTS函数语法专属约束

约束类型 禁用写法 正确替代方案 约束原因
函数嵌套 普通函数内嵌套普通函数 外层普通函数+内层箭头函数 避免作用域混乱
匿名函数 const fn = function() {} 箭头函数const fn = () => {} 禁用普通匿名函数,强制简化写法
隐式any类型 未标注类型的参数/变量 显式声明参数/变量类型 静态类型校验,提前拦截错误
函数重载 箭头函数尝试重载 改用传统函数实现重载 箭头函数不支持重载语法
无返回值标注 无返回值函数未写void 强制标注void 提升代码可读性,符合规范

约束合规示例

// 1. 合法的函数嵌套
function parentFunc(): void {
  const childFunc = () => console.log(`内层箭头函数执行`);
  childFunc(); // 输出:内层箭头函数执行
}
parentFunc();

// 2. 合法的匿名函数替代方案
const tripleNum = (num: number): number => num * 3;
console.log(`tripleNum(5) = ${tripleNum(5)}`); // 输出:15

// 禁用写法(注释对比)
// function parentFuncErr() { function childFuncErr() {} } // 普通函数嵌套
// const tripleNumErr = function(num: number) { return num * 3; }; // 普通匿名函数
// const tripleNumErr2 = (num) => num * 3; // 隐式any类型
// function printInfoErr(name: string, age: number) {} // 无返回值未标注void

八、函数调用入口代码

8.1 统一测试入口

// utils/FunctionBasicTest.ets
// 统一测试入口(函数定义分散在上述各章节中,此处仅保留调用逻辑)
export function allTest(): void {
  console.log("===== 基础函数调用 =====");
  console.log(`add(10,20) = ${add(10,20)}`);
  console.log(`multiply(5,6) = ${multiply(5,6)}`);
  printInfo("小明", 18);
  
  console.log("\n===== 箭头函数调用 =====");
  console.log(`subtract(20,8) = ${subtract(20,8)}`);
  printGreet("小红");
  
  console.log("\n===== 参数处理调用 =====");
  printUser("小李", 20);
  printUserDetail("小王", 22);
  printAddress("小赵");
  console.log(`getScoreLevel(85) = ${getScoreLevel(85)}`);
  
  console.log("\n===== 函数重载调用 =====");
  console.log(`sum(1,2,3) = ${sum(1,2,3)}`);
  console.log(`format(3.1415) = ${format(3.1415)}`);
  
  console.log("\n===== 箭头函数简化语法调用 =====");
  console.log(`addSimple(3,4) = ${addSimple(3,4)}`);
  console.log(`doubleNum(6) = ${doubleNum(6)}`);
  console.log(`getFullName("明", "张") = ${getFullName("明", "张")}`);
  
  console.log("\n===== 约束示例调用 =====");
  parentFunc();
  console.log(`tripleNum(5) = ${tripleNum(5)}`);
  
  console.log("\n✅ 所有函数测试执行完成");
}

8.2 Index.ets(页面调用)

// pages/Index.ets
import { allTest } from '../utils/FunctionBasicTest';

@Entry
@Component
struct Index {
  // 页面加载时执行测试
  aboutToAppear() {
    allTest();
  }

  build() {
    Column() {
      Text("函数基础测试")
        .fontSize(30)
        .fontWeight(FontWeight.Bold)
        .margin({ top: 50 });
        
      Text("测试结果请查看控制台日志")
        .fontSize(16)
        .margin({ top: 20 })
        .fontColor(Color.Grey);
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center);
  }
}

8.3 运行效果

===== 基础函数调用 =====
add(10,20) = 30
multiply(5,6) = 30
姓名:小明,年龄:18

===== 箭头函数调用 =====
subtract(20,8) = 12
你好,小红!

===== 参数处理调用 =====
姓名:小李,年龄:20
姓名:小王,年龄:22
小赵的城市:北京
getScoreLevel(85) = 良好

===== 函数重载调用 =====
sum(1,2,3) = 6
format(3.1415) = 3.14

===== 箭头函数简化语法调用 =====
addSimple(3,4) = 7
doubleNum(6) = 12
getFullName("明", "张") = 张明

===== 约束示例调用 =====
内层箭头函数执行
tripleNum(5) = 15

✅ 所有函数测试执行完成

九、核心总结

  1. 命名与类型:函数名用小驼峰+动词,参数/返回值必须显式声明类型,无返回值标注void
  2. 参数规则:必需参数在前,可选参数在后,默认参数建议放在两者之间;
  3. 箭头函数:单语句省略{}return,单参数保留括号+显式类型,禁用普通匿名函数;
  4. 函数重载:仅传统函数支持,重载签名按“精确→宽泛”排序,实现函数需覆盖所有场景;
  5. ArkTS约束:禁用普通函数嵌套、普通匿名函数,杜绝隐式any类型。

十、代码仓库

下节预告

本节我们学习了函数的定义与使用,但面对大型项目中复杂数据结构处理函数多参数复合返回值的场景时,仅靠基础的单一类型标注已无法满足严格的类型管控需求。
下一节我们将学习接口的核心约束与契约设计,这是为复杂数据和函数建立“类型契约”的核心章节,也是实现类型安全编程的关键工具:

  1. 拆解接口对复杂数据/多参数返回值的约束规则,搞懂“如何杜绝类型不匹配、字段混乱”的本质;
  2. 解析接口对函数参数/返回值的契约设计,为函数建立统一的类型规范(如运算/打印类函数);
  3. 揭秘ArkTS接口的专属语法约束,规避“函数签名接口、接口实例化”等高频语法坑;
  4. 结合函数章节的实战场景,落地接口对复杂数据、多参数返回值的全维度类型管控。
    这一节会帮你打通“函数+复杂数据+类型约束”的核心关联,彻底摆脱“类型不匹配、参数乱传、返回值不可控、数据结构混乱”的困扰!
posted @ 2026-01-19 15:04  鸿蒙-散修  阅读(0)  评论(0)    收藏  举报