10.29 ArkTS函数
函数的作用
函数在编程中是一个非常重要的概念,它具有以下几个主要作用:
-
代码重用:函数允许开发者将一段代码封装起来,这样可以在需要时多次调用,避免重复编写相同的代码,从而提高代码的复用性和开发效率。
-
模块化:通过将代码分解成多个函数,程序可以变得更加模块化。每个函数负责一个特定的任务,这样不仅使代码更易于理解和维护,也便于团队协作开发。
-
抽象化:函数可以将复杂的操作抽象为一个简单的接口,调用者只需要知道函数的功能和如何使用它,而不需要了解其内部的具体实现。
-
提高可读性:通过使用有意义的函数名和适当的注释,函数可以使代码更具可读性,便于其他开发者理解和使用。
-
逻辑分离:函数可以将程序的不同逻辑部分分离,使得代码结构清晰,便于调试和测试。
函数声明和调用
2.1没有参数的函数定义和调用
语法
//函数定义
function 函数名(){// 函数体
}//函数调用
函数名()
案例
//没有参数的函数定义和调用
function showMessage(){let msg = '欢乐中国年'let result = msg.split("")console.log(JSON.stringify(result))
}
console.log("准备执行")
// 调用函数以执行其函数体
showMessage()
-
形参:在声明函数的小括号里面是形参(形式上的参数)
-
实参:在调用函数的小括号里面是实参(实际上的参数)
-
在函数声明中,必须为每个参数标记类型。
语法
//函数定义
function 函数名(形参1: 类型,形参2: 类型 ... ){// 函数体
}//函数调用
函数名(实参1, 实参2 ...)
案例
// 调用函数以执行其函数体,实参值会赋值给函数的形参。
function add(x: number, y: number){let result = x + yconsole.log(result.toString())
}
add(23,45)
函数的作用域
作用域是指变量、函数等在代码中的可访问区域。作用域的主要目的是隔离变量,防止在不同作用域下同名变量发生冲突
函数中定义的变量和函数的形参仅可以在函数内部访问,不能从外部访问。
当函数被调用时,会创建一个新的作用域。函数作用域内的变量只能在函数内部访问,函数执行完毕后,函数作用域会被销毁。在函数作用域中,变量首先在自身作用域中查找,如果没有找到,则会向上级作用域查找,直到全局作用域。
局部变量(在函数外部不能访问): 1. 函数的形参 2. 在函数内部声明的变量或常量
代码一:
let num = 10
function fun1(age: number){console.log(age.toString())let count = 9num++console.log(num.toString()) // 10console.log(a) //报错
}
fun1(99)
fun1(70)
//报错,因为在函数外部不能访问函数内部声明的变量
// console.log(count)
代码二:
如果函数中定义的变量与外部作用域中已有变量同名,则函数内的局部变量定义将覆盖外部定义。
let num = 10
function fun1(){let count = 9let num = 23// 如果函数console.log(num.toString()) // 23
}
fun1()
注意: 在ArkTS中不允许函数嵌套,但JS是允许的
举例:
![[d73a06bc-5a57-440d-b654-30fa79655e87.png]]
函数的返回值
通过return语句可以实现在函数外部访问到函数内部的数据
function handleWeek() {let today = new Date()let day = today.getDay() // 0表示星期日let week = ''switch (day) {case 1:week = '一'break;case 2:week = '二'break;case 3:week = '三'break;case 4:week = '四'break;case 5:week = '五'break;case 6:week = '六'break;default:week = '日'}return week
}let outerWeek = handleWeek()
console.log(outerWeek)
- return语句之后的内容不再执行。
function add(price: number, count: number) {let total = price * countreturn totalconsole.log('end') // return后面的语句不再执行
}
let result = add(2,3)
console.log(result.toString())
- return只返回一个值,可以用对象的方式返回多个值
interface DataModel{name: string,age: number
}
function test() {let name = '华为'let age = 20let data: DataModel = {name: name,age: age}return data
}
let result = test()
console.log(JSON.stringify(result))
console.log(result.name)
- 如果函数有return,则返回return后面的值,如果函数没有return,则函数返回的结果undefined。
function test() {let name = '华为'let age = 20
}
let result = test()
console.log(typeof result) // undefined
函数的参数
可选参数
如果参数为可选参数,那么允许在调用函数时省略该参数。
可选参数的格式可为name?: Type。
function hello(name?: string) {if (name == undefined) {console.log('Hello!');} else {console.log(`Hello, ${name}!`);}
}
hello()
hello("Amy")
可选参数的另一种形式为设置的参数默认值。如果在函数调用中这个参数被省略了,则会使用此参数的默认值作为实参。
function multiply(n: number, coeff: number = 2): number {return n * coeff;
}
multiply(2); // 返回2*2
multiply(2, 3); // 返回2*3
Rest参数
函数的最后一个参数可以是rest参数。使用rest参数时,允许函数或方法接受任意数量的实参。
function sum(...numbers: number[]): number {let res = 0;for (let n of numbers)res += n;return res;
}sum(); // 返回0
sum(1, 2, 3); // 返回6function sum(x: string,...numbers: number[]): number {let res = 0;// n代表数组中的每一个元素for (let n of numbers) {res += n;}return res;
}sum('b'); // 返回0
sum('a',1, 2, 3); // 返回6
回调函数
回调函数是一种将函数作为参数传递给另一个函数的技术,这个被传递的函数在适当的时机被调用。
//代码一
function f1(callback: ()=>void) {console.log('f1')if (Math.random() > 0.5){f2()}
}
function f2(){console.log('f2')
}//代码二
function fun1(s: (x:number)=>void){if (Math.random() > 0.5){s(5)}else{console.log('没有调用函数')}
}
function show(z: number){console.log('show',z.toString())
}
fun1(show)@Entry
@Component
struct Index {build() {Column() {Button('处理').onClick(() => {f1(f2)})}}
}
返回类型
如果可以从函数体内推断出函数返回类型,则可在函数声明中省略标注返回类型。
// 显式指定返回类型
function foo(): string { return 'foo'; }// 推断返回类型为string
function goo() { return 'goo'; }
不需要返回值的函数的返回类型可以显式指定为void或省略标注。这类函数不需要返回语句。
以下示例中两种函数声明方式都是有效的:
function hi1() { console.log('hi'); }
function hi2(): void { console.log('hi'); }
函数类型(类型定义)
函数类型通常用于定义回调:
//---------------------------type类型别名
type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;
function getName(n: NameOrResolver): Name {if (typeof n === 'string') {return n;} else {return n();}
}//------------------------------------定义函数类型
type trigFunc = (x: number) => number // 这是一个函数类型function do_action(f: trigFunc) {f(3.141592653589); // 调用函数
}do_action(Math.sin); // 将函数作为参数传入
箭头函数(又名Lambda函数)
函数可以定义为箭头函数,例如:
let sum = (x: number, y: number): number => {return x + y;
}
箭头函数的返回类型可以省略;省略时,返回类型通过函数体推断。
表达式可以指定为箭头函数,使表达更简短,因此以下两种表达方式是等价的:
let sum1 = (x: number, y: number) => { return x + y; }
let sum2 = (x: number, y: number) => x + y
箭头函数案例代码
()=>{}
function sum(x: number, y: number): number {return x + y;
}
let sum1 = (x: number, y: number): number => {return x + y;
}
let result = sum1(2,3)
console.log(result.toString())let fun1 = (x: number)=>{x++console.log(x.toString())
}let fun2 = (x: number)=>{x++console.log(x.toString())return x
}
// 箭头函数内部如何只有一条return语句,代码可以简化,去掉{}和return
let fun3 = (x: number)=> x++
let result3 = fun3(3)interface Person {name: string,age: number
}// 等价于return {}
let fun4 = (x: number): Person => ({name: 'aa',age: x}
)
闭包
闭包是由函数及声明该函数的环境组合而成的。该环境包含了这个闭包创建时作用域内的任何局部变量。
在下例中,f函数返回了一个闭包,它捕获了count变量,每次调用z,count的值会被保留并递增。
function f(): () => number {let count = 0;let g = (): number => { count++; return count; };return g;
}let z = f();
z(); // 返回:1
z(); // 返回:2
闭包的案例代码
// 闭包
// 计数器// 案例:------------------------------------ 全局变量
// 全局变量容易被污染
// let count = 0
// function counter(){
// // 局部变量不会被污染,但不能再累加,实际原因是函数调用完毕,局部变量被销毁
// count++
// console.log(count.toString())
// }// 案例:------------------------------------ 局部变量
// function counter(){
// // 局部变量不会被污染,但不能再累加,实际原因是函数调用完毕,局部变量被销毁
// let count = 0
// count++
// console.log(count.toString())
// }//案例: --------------------------------------闭包
function counter(){// 函数内部被return出去的箭头函数就是闭包// 因为闭包函数被暴露到全局,所以counter调用完毕,没有被销毁, 那么同时闭包引用的父函数的局部变量也没有被销毁let count = 0let g = ()=>{count++console.log(count.toString())}return g
}
let c1 = counter()
let c2 = counter()@Entry
@Component
struct Index {@State result: number = 0build() {Column() {Button(`计数器1:`).onClick(() => {c1()})Button(`计数器2:`).onClick(() => {c2()})}}
}
函数重载(类型定义)
我们可以通过编写重载,指定函数的不同调用方式。具体方法为,为同一个函数写入多个同名但签名不同的函数头,函数实现紧随其后。
function foo(x: number): void; /* 第一个函数定义 */
function foo(x: string): void; /* 第二个函数定义 */
function foo(x: number | string): void { /* 函数实现 */
}foo(123); // OK,使用第一个定义
foo('aa'); // OK,使用第二个定义//需求分析: 希望foo这个函数,实现的功能是:
// 传入一个number, 返回值为number
// 传入一个string, 返回值为string
function foo(x: number): number; /* 第一个函数定义 */
function foo(x: string): string;
function foo(x: number | string): number | string { /* 函数实现 */return x
}
// 数值.toFiex(2) 作用是四舍五入保留指定的小数位
let r1 = foo(1.8963678)
let r2 = foo('a')
不允许重载函数有相同的名字以及参数列表,否则将会编译报错。
