零基础鸿蒙应用开发第十三节:函数的定义与使用
前面已经多次用到函数,但从未系统全面讲解。函数是ArkTS代码组织的核心单元,也是实现逻辑封装、复用、解耦的基础工具。本节会帮你系统掌握ArkTS中函数的标准定义规范、参数处理规则、箭头函数简化写法,建立“规范编码、场景化用函数”的编程思维。
【学习目标】
- 理解函数封装、复用、解耦的核心价值,掌握传统函数与箭头函数的标准定义语法
- 熟练处理必需参数、默认参数、可选参数的语法规则,实现函数的灵活调用
- 理解函数重载的底层逻辑,适配多参数类型/个数的业务场景
- 掌握ArkTS函数类型约束核心规则,规避隐式any、匿名函数等语法错误
- 区分传统函数与箭头函数的适用场景,熟练运用箭头函数的简化写法
【学习重点】
- 函数基础:传统函数/箭头函数的定义规范与调用逻辑
- 参数规则:必需→默认→可选的参数顺序及编译期类型校验
- 函数重载:传统函数专属的多场景适配能力及优先级规则
- 箭头函数:简化语法核心规则(单参数/单语句/无参数)
- 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 函数调用核心注意事项
- 调用时参数的个数、类型必须与声明完全匹配,否则编译报错
- 有返回值函数可接收结果(
const res = add(1,2))或直接调用(add(1,2)) - 无返回值函数仅支持直接执行(
printInfo("小明", 18)) - 所有参数/返回值必须显式声明类型,禁止隐式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;
五、箭头函数的简化语法
核心简化规则
- 单语句函数体:省略
{}和return - 单参数:必须显式声明类型,且建议保留括号(ArkTS推荐写法)
- 无参数:保留空括号
() - 禁止隐式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
✅ 所有函数测试执行完成
九、核心总结
- 命名与类型:函数名用小驼峰+动词,参数/返回值必须显式声明类型,无返回值标注
void; - 参数规则:必需参数在前,可选参数在后,默认参数建议放在两者之间;
- 箭头函数:单语句省略
{}和return,单参数保留括号+显式类型,禁用普通匿名函数; - 函数重载:仅传统函数支持,重载签名按“精确→宽泛”排序,实现函数需覆盖所有场景;
- ArkTS约束:禁用普通函数嵌套、普通匿名函数,杜绝隐式any类型。
十、代码仓库
- 工程名称:FunctionBasicDemo
- 仓库地址:https://gitee.com/juhetianxia321/harmony-os-code-base.git
下节预告
本节我们学习了函数的定义与使用,但面对大型项目中复杂数据结构处理、函数多参数复合返回值的场景时,仅靠基础的单一类型标注已无法满足严格的类型管控需求。
下一节我们将学习接口的核心约束与契约设计,这是为复杂数据和函数建立“类型契约”的核心章节,也是实现类型安全编程的关键工具:
- 拆解接口对复杂数据/多参数返回值的约束规则,搞懂“如何杜绝类型不匹配、字段混乱”的本质;
- 解析接口对函数参数/返回值的契约设计,为函数建立统一的类型规范(如运算/打印类函数);
- 揭秘ArkTS接口的专属语法约束,规避“函数签名接口、接口实例化”等高频语法坑;
- 结合函数章节的实战场景,落地接口对复杂数据、多参数返回值的全维度类型管控。
这一节会帮你打通“函数+复杂数据+类型约束”的核心关联,彻底摆脱“类型不匹配、参数乱传、返回值不可控、数据结构混乱”的困扰!
浙公网安备 33010602011771号