ArkTS基础语法 |(2)函数
在学习HarmonyOS开发的核心语言ArkTS时,整理了一份基础语法笔记,方便日后回顾。
函数能够封装重复的业务逻辑(代码块),遵循先定义、后调用的基本原则,大幅提升代码的复用率和可维护性。
一、函数声明
ArkTS中声明函数需明确函数名、参数列表、返回类型(可选)和函数体
- 声明函数的核心规则:
-
形参必须显式标记类型,实参的数量、类型需与形参一一对应。
-
仅定义函数不调用,函数体代码不会执行。
-
支持无参无返回值、带参带返回值等多种形式。
-
可通过
return关键字返回处理结果,无返回值时可省略return。
1. 无参无返回值函数
最基础的函数形式,无参数传入,也无结果返回,适用于执行简单的固定逻辑。
// 定义语法
function 函数名() {
函数体
}
// 调用语法
函数名()
// 示例
function sayHello() {
console.log('Hello ArkTS!'); // 执行简单的打印逻辑
}
sayHello(); // 调用后输出:Hello ArkTS!
2. 带参带返回值函数
适用于需要传入参数并返回处理结果的场景,需指定形参类型和函数返回类型。
-
形参:函数定义时的参数,必须指定类型。
-
实参:函数调用时传入的参数,需与形参数量、类型严格匹配。
-
return :用于返回处理结果,执行到
return后函数体立即终止。
// 示例:定义求和函数,接收两个number类型参数,返回number类型结果。
function sum(num1: number, num2: number): number {
return num1 + num2; // 返回两数之和
}
// 调用函数,接收返回值并打印。
let result: number = sum(10, 20);
console.log(result); // 输出:30
二、函数参数
ArkTS对函数参数做了灵活扩展,支持必选参数、可选参数、默认值参数、rest参数,其中可选参数、默认值参数、rest参数均需遵循特定的语法规则,且只能出现在必选参数之后。
1. 可选参数
调用函数时可省略的参数
语法:参数名?: 类型
省略时参数值为undefined,需在函数体内做判空处理。
- 注意:可选参数必须跟在必选参数后面,不能放在必选参数之前。
// 示例
function hello(name?: string) {
if (name == undefined) {
console.info('Hello!'); // 省略参数时执行
} else {
console.info(`Hello, ${name}!`); // 传入参数时执行
}
}
hello(); // 省略参数 输出:Hello!
hello('ArkTS'); // 传入参数 输出:Hello, ArkTS!
2. 默认值参数
可选参数的另一种形式,为参数设置默认值,调用时若省略该参数,会自动使用默认值作为实参.
语法:参数名: 类型 = 默认值
- 注意:默认值参数也需跟在必选参数之后,若调用时传入新值,会覆盖默认值。
// 示例:定义乘法函数,系数coeff设置默认值2。
function multiply(n: number, coeff: number = 2): number {
return n * coeff;
}
multiply(2); // 省略coeff,使用默认值2,返回:4。
multiply(2, 3); // 传入coeff=3,覆盖默认值,返回:6。
3. rest参数
用于处理不定数量的参数输入,允许函数接收一个不定长的数组。
语法...rest参数名: 类型[]
核心规则:
- rest参数只能是函数的最后一个参数
- rest参数的类型必须是数组类型
- 调用时可传入0个、1个或多个同类型参数,均会被封装为数组。
// 示例:定义求和函数,支持传入任意多个number类型参数.
function sum(...numbers: number[]): number {
let res = 0;
// 遍历rest参数数组,累加求和
for (let n of numbers) {
res += n;
}
return res;
}
sum(); // 传入0个参数 返回:0
sum(1, 2, 3); // 传入3个参数 返回:6
sum(10, 20, 30, 40); // 传入4个参数 返回:100
三、函数返回类型
ArkTS中函数的返回类型支持显式指定和自动推断,无返回值时可指定为void类型.
核心规则:
- 若函数体的返回结果可被编译器推断,可省略返回类型标注。
- 无返回值的函数,可显式指定
void或省略返回类型。 - 显式指定返回类型后,函数返回的结果必须与该类型匹配,否则编译报错。
1. 显式指定返回类型
适用于需要明确函数返回值类型的场景,提升代码的可读性和规范性。
// 显式指定返回string类型
function foo(): string {
return 'foo';
}
// 显式指定返回number类型
function getNum(): number {
return 100;
}
2. 自动推断返回类型
编译器可根据函数体的return语句自动推断返回类型,可省略返回类型标注,简化代码。
// 自动推断返回类型为string
function goo() {
return 'goo';
}
// 自动推断返回类型为boolean
function isTrue() {
return 1 > 0;
}
3. 无返回值(void类型)
表示函数无任何返回结果,即使写return也不能携带值,两种声明方式均可,效果一致。
// 方式1:省略返回类型标注(简洁写法)
function hi1() {
console.info('hi');
}
// 方式2:显式指定void类型(规范写法)
function hi2(): void {
console.info('hi');
// 无返回值的函数可写空return,不可写return 123(编译报错)。
return;
}
4. 拓展:never类型
表示函数永远不会执行完成(如函数内抛出异常、无限循环),是比void更严格的返回类型,基础开发中使用较少,做简单了解即可。
function errorFunc(): never {
throw new Error('程序执行异常');
}
四、函数的作用域
ArkTS中函数遵循块级作用域规则,函数内定义的变量/实例为局部变量,函数外定义的为全局变量。 核心规则:
- 局部变量仅能在函数内部访问,外部无法访问。
- 若函数内的局部变量与全局变量同名,局部变量会覆盖全局变量(函数内优先使用局部变量)。
- 函数内定义变量时,必须使用
let/const,否则会被提升为全局变量。
// 定义全局变量
let outerVar = 'I am outer ';
function func() {
// 定义同名局部变量,覆盖全局变量。
let outerVar = 'I am inside';
console.info(outerVar); // 输出:I am inside(使用局部变量)
}
func();
console.info(outerVar); // 输出:I am outer(全局变量未被修改)
// 易错点:函数内未用let/const定义的变量,会成为全局变量。
function badFunc() {
testVar = '全局变量'; // 未用let/const,提升为全局变量
}
badFunc();
console.info(testVar); // 输出:全局变量
五、函数的调用
函数定义后,需通过 函数名(实参) 的方式调用,才能执行函数体代码。
核心规则:
- 实参的数量、类型需与形参严格匹配(可选参数、rest参数除外)。
- 函数调用时,实参会按顺序赋值给形参。
- 带返回值的函数,调用时可通过变量接收返回结果,也可直接调用(忽略返回结果)。
// 定义函数:拼接两个字符串
function join(x: string, y: string): string {
let z: string = `${x} ${y}`;
return z;
}
// 方式1:接收返回值(推荐)
let res = join('hello', 'world');
console.info(res); // 输出: hello world
// 方式2:直接调用,忽略返回结果。
join('Hi', 'ArkTS');
拓展:参数的传递方式
ArkTS中参数传递分为值传递和引用传递,取决于参数的类型。
- 值传递:适用于基本类型(number、string、boolean、undefined、null),传递的是变量的副本,函数内修改副本不会影响原变量。
- 引用传递:适用于引用类型(对象、数组),传递的是变量的内存地址,函数内修改对象/数组的属性,会影响原变量。
// 值传递(基本类型)
function changeNum(num: number) {
num = 100;
}
let a = 10;
changeNum(a);
console.log(a); // 输出:10(原变量未被修改)
// 引用传递(引用类型)
function changeObj(obj: {name: string}) {
obj.name = 'ArkTS';
}
let person = {name: '鸿蒙'};
changeObj(person);
console.log(person.name); // 输出:ArkTS(原对象被修改)
六、函数类型
ArkTS中函数也是一种数据类型,可通过type关键字定义函数类型,用于约束函数的参数列表和返回类型,最常用的场景是定义回调函数,让代码的类型约束更严格。
函数类型的定义语法
type 函数类型名 = (参数1: 类型1, 参数2: 类型2, ...) => 返回类型; // 语法格式
示例:定义回调函数
// 定义函数类型:接收2个number类型参数,返回number类型结果。
type AddFunc = (a: number, b: number) => number;
// 定义函数,参数是AddFunc类型的回调函数。
function calculate(a: number, b: number, back: AddFunc) {
return back(a, b); // 调用回调函数 传入a和b
}
// 传入符合AddFunc类型的函数作为实参(求和)
const add = (x: number, y: number) => x + y;
console.log(calculate(2, 3, add)); // 输出:5(符合规矩 正常执行)
// 若传不符合规矩的函数(比如返回string),编译直接报错(这就是函数类型的作用!)。
const badAdd = (x: number, y: number) => '${x+y}';
calculate(2,3, badAdd); // 报错:返回类型string不符合AddFunc的number类型
函数类型的赋值
定义函数类型后,可将符合该类型的函数赋值给变量,约束变量的函数形态。
// 定义函数类型
type AddFunc = (a: number, b: number) => number;
// 定义符合该类型的函数
let add: AddFunc = (x, y) => x + y;
// 调用
console.log(add(1,2)); // 输出:3
七、箭头函数(Lambda函数)
箭头函数是普通函数的简洁写法,语法更精炼、代码更简洁,适合编写短小的函数逻辑,也是鸿蒙开发中高频使用的写法。
- 核心特点:无自己的this(继承外层作用域的this)
1. 基本语法
// 完整语法
let 函数名 = (形参1: 类型1, 形参2: 类型2) => {
函数体;
return 结果;
}
简写规则:
- 单表达式函数体,可省略大括号
{}和return。 - 单个形参,可省略小括号
()。 - 无参或多个形参,必须保留小括号
()。 - 返回类型可省略,由编译器自动推断。
2. 常见使用形式
(1)无参箭头函数
// 完整写法
let sayHi = () => {
console.log('Hi, 箭头函数');
};
// 调用
sayHi(); // 输出:Hi,箭头函数
// 无参+单表达式(若有返回值)
let getStr = () => 'Hello Arrow Function';
console.log(getStr()); // 输出:Hello Arrow Function
(2)带参箭头函数
// 多个形参完整写法
let multiply = (num1: number, num2: number): number => {
return num1 * num2;
};
let res = multiply(5, 6);
console.log(res); // 输出:30
// 单个形参简写(省略小括号、大括号、return)
let double = (n: number) => n * 2; // 标准简写(推荐写法)
let double2 = n => n * 2; // 不推荐写法
// 多个形参 单表达式 简写
let sum = (a: number, b: number) => a + b;
console.log(sum(3,4)); // 输出:7
3. 箭头函数的核心特性
箭头函数的核心差异是无自身的this,鸿蒙开发中在组件生命周期、事件回调中使用时需特别注意:
- 箭头函数的
this继承自外层作用域,不会随调用方式改变。(外层无目标属性则输出undefined) - 普通函数的
this指向调用者,调用方式不同,this指向不同。(调用者无目标属性也可能输出undefined) - 箭头函数不能作为构造函数,不能使用
new关键字调用。
// 示例:箭头函数继承外层this
let obj = {
name: 'ArkTS',
fn1: function() {
// 普通函数:this指向obj
console.log(this.name);
},
fn2: () => {
// 箭头函数:this继承外层(全局作用域)(无name属性)
console.log(this.name);
}
};
obj.fn1(); // 调用普通函数fn1 输出:ArkTS
obj.fn2(); // 调用箭头函数fn2 输出:undefined
八、闭包
闭包是由函数和声明该函数的环境组合而成的整体,该环境包含了闭包创建时作用域内的所有局部变量。
- 核心特性:闭包可以访问并保留外层函数的局部变量,即使外层函数执行完毕,局部变量也不会被销毁。
1. 闭包的实操示例
// 外层函数f
function f(): () => number {
let count = 0; // 外层函数的局部变量
// 内层箭头函数g 形成闭包
let g = (): number => {
count++; // 访问外层函数的局部变量count
return count;
};
return g; // 返回内层函数g
}
let z = f(); // 外层函数执行完毕 返回内层函数g
// 多次调用z 闭包保留count的状态 持续递增
console.log(z()); // 返回:1
console.log(z()); // 返回:2
console.log(z()); // 返回:3
2. 闭包的使用场景
- 封装私有变量:隐藏内部变量,仅通过闭包暴露操作方法,避免全局变量污染。
- 保留函数执行状态:如闭包实操示例中的计数器,多次调用可保留上一次的执行结果。
- 鸿蒙开发中:组件事件回调、定时器中保留上下文状态。
3. 闭包的注意事项
闭包会保留外层函数的变量,导致变量不会被垃圾回收,若过度使用或不当使用,会造成内存泄漏。 注意事项:
- 避免在循环中创建闭包。
- 不需要使用闭包时,及时解除引用(如将闭包变量赋值为
null)。
4. 闭包封装私有变量示例
// 封装计数器 仅暴露增/减方法 隐藏count变量
function createCounter() {
let count = 0; // 私有变量 外部无法访问
return {
add: () => count++, // 闭包:增加计数
sub: () => count--, // 闭包:减少计数
get: () => count // 闭包:获取计数
};
}
let counter = createCounter();
counter.add();
counter.add();
console.log(counter.get()); // 输出:2
counter.sub();
console.log(counter.get()); // 输出:1
// 外部无法直接修改count 避免变量污染
九、函数重载
函数重载指为同一个函数定义多个不同的签名(参数列表/返回类型不同),让同一个函数支持多种调用方式。 在ArkTS中编译器会根据实参的类型/数量,匹配对应的重载签名执行。
1. 函数重载的语法规则
- 先编写多个重载声明(同名、不同签名、无函数体)
- 最后编写一个函数实现(必须包含所有重载声明的参数/返回类型,用联合类型表示)。
- 重载声明的参数列表不能相同,否则编译报错。
- 函数实现的参数类型需覆盖所有重载声明的参数类型,返回类型也需匹配。
2. 函数重载的实操示例
// 重载声明1:接收一个number类型参数 无返回值
function foo(x: number): void;
// 重载声明2:接收一个string类型参数 无返回值
function foo(x: string): void;
// 函数实现:参数类型为 “ number | string ” 覆盖所有重载声明
function foo(x: number | string): void {
if (typeof x === 'number') {
console.log('数字类型:', x);
} else {
console.log('字符串类型:', x);
}
}
// 调用:编译器自动匹配对应的重载声明
foo(123); // 匹配重载1 输出: 数字类型: 123
foo('aa'); // 匹配重载2 输出: 字符串类型: aa
3. 带返回值的函数重载示例
// 重载声明1:两个number参数 返回number
function calc(a: number, b: number): number;
// 重载声明2:两个string参数 返回string
function calc(a: string, b: string): string;
// 函数实现
function calc(a: number | string, b: number | string): number | string {
if (typeof a === 'number' && typeof b === 'number') {
return a + b;
} else {
return `${a}${b}`;
}
}
console.log(calc(1,2)); // 输出:3
console.log(calc('a','b')); // 输出:ab
4. 函数重载的注意事项
- 函数重载仅在编译阶段生效,用于做类型校验,运行时只有一个函数实现。
- 不能只有重载声明而无函数实现,否则编译报错。
- 鸿蒙开发中,函数重载常用于封装通用方法,让方法支持多种参数类型,提升代码的灵活性。