ArkTS的进阶语法-2(模块化,定时器,递归)

208 阅读9分钟

ArkTS的进阶语法

(今天更新的是模块化,定时器,递归)

1. 泛型

​ 泛型在保证类型安全(不丢失类型信息)的同时,可以让函数等与多种不同的类型一起工作,灵活可复用

**通俗一点就是:类型是可变的!

1.泛型函数

顾名思义就是,泛型和函数结合到一起使用

Type 是泛型参数的名字,类似于之前的形参,

  • 可以自行定义,有意义即可

  • 首字母大写

  • 参见泛型参数名 T、Type

    // 函数定义
    function identity<Type>(arg: Type): Type {
      return arg;
    }
    
    // 在使用的时候传入具体的类型,函数就可以正常工作啦~
    identity<string>('123')
    identity<number>(123)
    identity<boolean>(false)
    identity<string[]>(['1', '2', '3'])
    
    

    结合 编译器 的 类型推断 功能,在使用函数的时候还可以进行简写,比如下面的写法,和上面的是一样的。

    虽然大部分时候可以推断出类型,但是如果碰到 编译器 无法推断类型时,就需要显式传入类型参数,这在更复杂的示例中可能会发生。

identity('123')
identity(123)
identity(false)
identity(['1', '2', '3'])

使用泛型变量

类型变量可以用在任意支持€w3e4rtuy1256789 -=`的位置,实现更为复杂的功能

//将类型变量 Type,作为数组项的类型即可
function identity<Type>(arr: Type[]): number {
  return arr.length
}

2.使用泛型约束

如果开发中不希望任意的类型都可以传递给 **类型参数 ,**就可以通过泛型约束来完成

核心步骤:

  1. 定义用来约束的 接口(interface)

  2. 类型参数通过 extends 即可实现约束

    interface 接口{
      属性:类型
    }
    function 函数<Type extends 接口>(){}
    
    // 后续使用函数时,传入的类型必须要有 接口中的属性
    ------试一试
    interface ILengthwise {
      length: number
    }
    
    function identity<Type extends ILengthwise>(arr: Type) {
      console.log(arr.length.toString())
    }
    
    // 使用的时候 只要有 length 属性即可
    identity([1, 2, 3, 4]) // 数组有 length 属性 正常运行
    identity('1234') // 字符串也有 length 属性 正常运行
    
    // identity(124) // 数值没有 length 属性 报错
    
    class Cloth implements ILengthwise {
      length: number = 10
    }
    
    class Trousers {
      length: number = 110
    }
    
    identity(new Cloth()) // Cloth 有 length 属性 正常运行
    identity(new Trousers()) // Trousers 有 length 属性 正常运行
    
    

3.多个泛型参数

日常开发的时候,如果有需要可以添加多个 类型变量,只需要定义并使用 多个类型变量即可

function funcA<T, T2>(param1: T, param2: T2) {
  console.log('参数 1', param1)
  console.log('参数 2', param2)
}

funcA<string, number>('张三', 18)
funcA<string[], boolean[]>(['张三','李四'], [false])


4.泛型接口

定义接口时结合泛型,那么这个接口就是 泛型接口

interface 接口<Type>{
  // 内部使用Type
}
interface IdFunc<Type> {
  id: (value: Type) => Type
  ids: () => Type[]
}

let obj: IdFunc<number> = {
  id(value) { return value },
  ids() { return [1, 3, 5] }
}


5.泛型类

和泛型接口类似,如果定义类的时候结合泛型,那么这个类就是 泛型类

class 类名<Type>{
  // 内部可以使用 Type
}
-----试一试
// 定义
class Person <T> {
  id: T

  constructor(id: T) {
    this.id = id
  }

  getId(): T {
    return this.id
  }
}

// 使用
let p = new Person<number>(10)


2.工具类型

ArkTS提供了4 中工具类型,来帮组我们简化编码

4 种工具类型中,熟练掌握 Partial 即可,可以简化编码

1.partial

(partial:部分)

基于传入的Type类型构造一个[新类型],将Type的所有属性设置为可选。

(泛型约束太复杂了这个完全可以替代他)

type 新类型 = Partial<接口>
type 新类型 = Partial<类>

// 后续使用新类型即可
interface Person {
  name: string 
  age: number 
  friends: string[]
}

type ParPerson = Partial<Person>

// 因为都是可选的,可以设置为空对象
let p: ParPerson = {}

2.Required

基于传入的Type类型构造一个【新类型】,将 Type 的所有属性设置为必填

type 新类型 = Required<接口>
type 新类型 = Required<类>

// 后续使用新类型即可
class Person {
  name?: string
  age?: number
  friends?: string[]
}

type RequiredPerson = Required<Person>

// 都是必须得属性,必须设置值
let p: Required<Person> = {
  name: 'jack',
  age: 10,
  friends: []
}

3.Readonly

基于 Type构造一个【新类型】,并将Type 的所有属性设置为readonly

type 新类型 = Readonly<接口>
type 新类型 = Readonly<类>

// 后续使用新类型即可
class Person {
  name: string = ''
  age: number = 0
}

type ReadonlyIPerson = Readonly<Person>

const p: ReadonlyIPerson = {
  name: 'jack',
  age: 10
}

p.name = 'rose' // 报错 属性全部变成只读

4.Record<Keys,Type>

构造一个对象类型,其属性键为Keys,属性值为Type。该实用程序可用于将一种类型的属性映射到另一种类型。

class CatInfo {
  age: number = 0
  breed: string = ''
}

// 联合类型
type CatName = "miffy" | "boris" | "mordred";

// 通过Record 构建新的对象类型
// 属性名:必须是CatName 中的值
// 属性值:必须是CatInfo 类型
type Ctype = Record<CatName, CatInfo>

const cats: Ctype = {
  'miffy': { age: 5, breed: "Maine Coon" },
  'boris': { age: 5, breed: "Maine Coon" },
  'mordred': { age: 16, breed: "British Shorthair" },
};

3.空安全

默认情况下,ArkTS中的所有类型都是不可为空的。如果要设置为空,需要进行特殊的处理,并且在获取 可能为空的值的时候也需要特殊处理

1.联合类型设置为空

let x: number = null;    // 编译时错误
let y: string = null;    // 编译时错误
let z: number[] = null;  // 编译时错误
通过联合类型指定为空即可,使用时可能需要判断是否为空
// 通过联合类型设置为空
let x: number | null = null;
x = 1;    // ok
x = null; // ok
// 取值的时候,根据需求可能需要考虑 屏蔽掉空值的情况
if (x != null) { /* do something */ }

2.非空断言运算符

后缀运算符! 可用于断言其操作数为非空。

应用于空值时,运算符将抛出错误。否则,值的类型将从T | null更改为T:

let x: number | null = 1;
let y: number
y = x + 1;  // 编译时错误:无法对可空值作加法
y = x! + 1; // 通过非空断言,告诉编译器 x不为 null

3.空值合并运算符

空值合并二元运算符?? 用于检查左侧表达式的求值是否等于null。如果是,则表达式的结果为右侧表达式;否则,结果为左侧表达式。

换句话说,a ?? b等价于三元运算符a != null ? a : b。

在以下示例中,getNick方法如果设置了昵称,则返回昵称;否则,返回空字符串:

class Person {
  name: string | null = null

  getName(): string {
    // return this.name === null ? '' : this.name
    // 等同于 如果 name不为空 就返回 name 反之返回 ''
    return this.name ?? ''
  }
}

4.可选链

在访问对象属性时,如果该属性是undefined或者null,可选链运算符会返回undefined。

(可选链?当前面的对象不为空或undefined才调用属性或方法)

class Dog {
  bark() {
    console.log('啊呜~')
  }
}

class Person {
  name?: string
  dog?: Dog

  constructor(name: string, dog?: Dog) {
    this.name = name
    this.dog = dog
  }
}

const p: Person = new Person('jack')

// p.dog.bark()// 报错 dog 可能为空

// 逻辑判断
if (p.dog) {
  p.dog.bark()
}

// 当 dog不为null 或 undefined 时 再调用 bark 并且不会报错
p.dog?.bark()

4.模块化🎈

  1. 把一个大的程序拆分成【互相依赖】的若干小文件
  2. 这些小文件还可以通过【特定的语法】组合到一起
  3. 这个过程称之为【模块化】

优点:

1.更好维护

2.更好的复用性

缺点:

需要学习模块化语法

分析:

  1. 功能写完只有10行代码,模块化没啥必要!
  2. 功能写完有100行,或者1000行代码,里面有【好几段逻辑】在其他地方也要用--模块化

1.默认导入与导出🏃‍♂️

ArkTS 中每个 ets 文件都可以看做是一个模块,默认只能在当前文件中使用,如果要在其他位置中使用就需要:

  1. 当前模块中 导出模块

  2. 需要使用的地方 导入模块

    // 默认导出 
    export default 需要导出的内容
    
    // 默认导入
    import xxx from '模块路径'
    
    //试一试
    // Model.ets
    // 一起写
    export default class Person {
      name: string = ''
    }
    
    // 分开写
    // export default Person
    
    // 只能有一个默认导出
    
    // TestModel.ets
    import Person from './model'
    
    const p: Person = new Person()
    
    

    路径

    / 表示进入某个文件夹里面
    
    .  表示当前文件所在文件夹                  ./
    
    .. 表示当前文件的上一级文件夹             ../
    
    

2.按需导入与导出🏃‍♂️

如果有很多的东西需要导出,可以使用按需导出,他也有配套的导入语法

// ---------- Model.ets ----------
const info: string = '信息'

// 单独写
export const num: number = 10

function sayHi() {
  console.log('你好吗~')
}

// 或者 写到 {} 内部
export {
  info, sayHi
}

// ---------- MainFile.ets ----------
import { info, num, sayHi as sayHello } from './Model'

console.log('info:', info)
// 起别名
sayHello()
console.log('num:', num + '')

3.全部导入🏃‍♂️

导出部分不需要调整,调整导入的语法即可

import * as Utils from './utils'
// 通过 Utils 即可获取 utils模块中导出的所有内容

5.定时器🎈

日常开发中如果需要代码 延迟一会执行,或者每隔一段时间执行一次,就可以使用定时器

定时器有两种:

  1. setTimeout: 延迟执行
  2. setInterval: 间隔执行

1.setTimeout(延迟执行)🏃‍♂️

setTimeout可以用来设置一个定时器,当设置的时间到了之后就会去执行指定的函数

执行一次用 setTimeout

setTimeout、clearTimeout是一个全局函数,可以直接使用

@Entry
@Component
struct Page01_timeoutAndInterval {
  timeId?: number

  build() {
    Column({ space: 20 }) {
      Text('延时器-Timeout')
        .fontSize(30)
        .width('100%')
        .textAlign(TextAlign.Center)
      Row({ space: 20 }) {
        Button('开启延时器')
          .onClick(() => {
            // 开启延时器
            let timeId = setTimeout(() => {
              // 延迟执行逻辑
              console.log('代码执行啦')
            }, 2000)

            //  保存定时器 id
            this.timeId = timeId
          })
        Button('关闭延时器')
          .onClick(() => {
            // 调用 clearTimeout 清除定时器
            clearTimeout(this.timeId)
          })
      }
    }
    .padding(10)
  }
}

2.setInterval(间隔执行)🏃‍♂️

setInterval 也是可以用来设置一个定时器,根据设置的时间间隔来执行指定的函数

执行多次用 setInterval!!

setInterval、clearInterval是一个全局函数,可以直接使用

// 1. 基本使用
setInterval(()=>{
  // 延迟执行的逻辑
},时间)

// 2. 获取返回的定时器 id
const timeId = setInterval(()=>{
  // 延迟执行的逻辑
},时间)

// 3. 根据定时器 id 清除定时器
clearInterval(timeId)
//试一试
@Entry
@Component
struct Page02_interval{
  // 只是用来保存数据,不影响 UI 可以不加状态修饰符
  timeId?: number

  build() {
    Column({ space: 20 }) {
      Text('定时器-Interval')
        .fontSize(30)
        .width('100%')
        .textAlign(TextAlign.Center)
      Row({ space: 20 }) {
        Button('开启延时器')
          .onClick(() => {
            this.timeId = setInterval(() => {
              console.log('代码执行啦~')
            }, 2000)
          })
        Button('关闭延时器')
          .onClick(() => {
            clearInterval(this.timeId)
          })
      }
    }
    .padding(10)
  }
}

6.递归🎈

调用自身的函数我们称之为递归函数

基本用法

在某种意义上说,递归近似于循环。两者都重复执行相同的代码,并且两者都需要一个终止条件(避免无限循环,或者在这种情况下更确切地说是无限递归)。

// 没有退出条件的 递归函数--无限递归
function func(){
  func()
}
//试一试
let num: number = 1

function printLog(num: number) {
  console.log(`你好,第${num}次打印`)
  // 递减
  num--
  // 退出条件
  if (num > 1) {
    printLog(num)
  }
}

// 打印 10 次
printLog(10)

---------------------------------------感觉写的不错的话就点点关注吧😘❤❤❤----------------------------------------------## ArkTS的进阶语法

(今天更新的是模块化,定时器,dig>)

1. 泛型

​ 泛型在保证类型安全(不丢失类型信息)的同时,可以让函数等与多种不同的类型一起工作,灵活可复用

**通俗一点就是:类型是可变的!

1.泛型函数

顾名思义就是,泛型和函数结合到一起使用

Type 是泛型参数的名字,类似于之前的形参,

  • 可以自行定义,有意义即可

  • 首字母大写

  • 参见泛型参数名 T、Type

    // 函数定义
    function identity<Type>(arg: Type): Type {
      return arg;
    }
    
    // 在使用的时候传入具体的类型,函数就可以正常工作啦~
    identity<string>('123')
    identity<number>(123)
    identity<boolean>(false)
    identity<string[]>(['1', '2', '3'])
    
    

    结合 编译器 的 类型推断 功能,在使用函数的时候还可以进行简写,比如下面的写法,和上面的是一样的。

    虽然大部分时候可以推断出类型,但是如果碰到 编译器 无法推断类型时,就需要显式传入类型参数,这在更复杂的示例中可能会发生。

identity('123')
identity(123)
identity(false)
identity(['1', '2', '3'])

使用泛型变量

类型变量可以用在任意支持€w3e4rtuy1256789 -=`的位置,实现更为复杂的功能

//将类型变量 Type,作为数组项的类型即可
function identity<Type>(arr: Type[]): number {
  return arr.length
}

2.使用泛型约束

如果开发中不希望任意的类型都可以传递给 **类型参数 ,**就可以通过泛型约束来完成

核心步骤:

  1. 定义用来约束的 接口(interface)

  2. 类型参数通过 extends 即可实现约束

    interface 接口{
      属性:类型
    }
    function 函数<Type extends 接口>(){}
    
    // 后续使用函数时,传入的类型必须要有 接口中的属性
    ------试一试
    interface ILengthwise {
      length: number
    }
    
    function identity<Type extends ILengthwise>(arr: Type) {
      console.log(arr.length.toString())
    }
    
    // 使用的时候 只要有 length 属性即可
    identity([1, 2, 3, 4]) // 数组有 length 属性 正常运行
    identity('1234') // 字符串也有 length 属性 正常运行
    
    // identity(124) // 数值没有 length 属性 报错
    
    class Cloth implements ILengthwise {
      length: number = 10
    }
    
    class Trousers {
      length: number = 110
    }
    
    identity(new Cloth()) // Cloth 有 length 属性 正常运行
    identity(new Trousers()) // Trousers 有 length 属性 正常运行
    
    

3.多个泛型参数

日常开发的时候,如果有需要可以添加多个 类型变量,只需要定义并使用 多个类型变量即可

function funcA<T, T2>(param1: T, param2: T2) {
  console.log('参数 1', param1)
  console.log('参数 2', param2)
}

funcA<string, number>('张三', 18)
funcA<string[], boolean[]>(['张三','李四'], [false])


4.泛型接口

定义接口时结合泛型,那么这个接口就是 泛型接口

interface 接口<Type>{
  // 内部使用Type
}
interface IdFunc<Type> {
  id: (value: Type) => Type
  ids: () => Type[]
}

let obj: IdFunc<number> = {
  id(value) { return value },
  ids() { return [1, 3, 5] }
}


5.泛型类

和泛型接口类似,如果定义类的时候结合泛型,那么这个类就是 泛型类

class 类名<Type>{
  // 内部可以使用 Type
}
-----试一试
// 定义
class Person <T> {
  id: T

  constructor(id: T) {
    this.id = id
  }

  getId(): T {
    return this.id
  }
}

// 使用
let p = new Person<number>(10)


2.工具类型

ArkTS提供了4 中工具类型,来帮组我们简化编码

4 种工具类型中,熟练掌握 Partial 即可,可以简化编码

1.partial

(partial:部分)

基于传入的Type类型构造一个[新类型],将Type的所有属性设置为可选。

(泛型约束太复杂了这个完全可以替代他)

type 新类型 = Partial<接口>
type 新类型 = Partial<类>

// 后续使用新类型即可
interface Person {
  name: string 
  age: number 
  friends: string[]
}

type ParPerson = Partial<Person>

// 因为都是可选的,可以设置为空对象
let p: ParPerson = {}

2.Required

基于传入的Type类型构造一个【新类型】,将 Type 的所有属性设置为必填

type 新类型 = Required<接口>
type 新类型 = Required<类>

// 后续使用新类型即可
class Person {
  name?: string
  age?: number
  friends?: string[]
}

type RequiredPerson = Required<Person>

// 都是必须得属性,必须设置值
let p: Required<Person> = {
  name: 'jack',
  age: 10,
  friends: []
}

3.Readonly

基于 Type构造一个【新类型】,并将Type 的所有属性设置为readonly

type 新类型 = Readonly<接口>
type 新类型 = Readonly<类>

// 后续使用新类型即可
class Person {
  name: string = ''
  age: number = 0
}

type ReadonlyIPerson = Readonly<Person>

const p: ReadonlyIPerson = {
  name: 'jack',
  age: 10
}

p.name = 'rose' // 报错 属性全部变成只读

4.Record<Keys,Type>

构造一个对象类型,其属性键为Keys,属性值为Type。该实用程序可用于将一种类型的属性映射到另一种类型。

class CatInfo {
  age: number = 0
  breed: string = ''
}

// 联合类型
type CatName = "miffy" | "boris" | "mordred";

// 通过Record 构建新的对象类型
// 属性名:必须是CatName 中的值
// 属性值:必须是CatInfo 类型
type Ctype = Record<CatName, CatInfo>

const cats: Ctype = {
  'miffy': { age: 5, breed: "Maine Coon" },
  'boris': { age: 5, breed: "Maine Coon" },
  'mordred': { age: 16, breed: "British Shorthair" },
};

3.空安全

默认情况下,ArkTS中的所有类型都是不可为空的。如果要设置为空,需要进行特殊的处理,并且在获取 可能为空的值的时候也需要特殊处理

1.联合类型设置为空

let x: number = null;    // 编译时错误
let y: string = null;    // 编译时错误
let z: number[] = null;  // 编译时错误
通过联合类型指定为空即可,使用时可能需要判断是否为空
// 通过联合类型设置为空
let x: number | null = null;
x = 1;    // ok
x = null; // ok
// 取值的时候,根据需求可能需要考虑 屏蔽掉空值的情况
if (x != null) { /* do something */ }

2.非空断言运算符

后缀运算符! 可用于断言其操作数为非空。

应用于空值时,运算符将抛出错误。否则,值的类型将从T | null更改为T:

let x: number | null = 1;
let y: number
y = x + 1;  // 编译时错误:无法对可空值作加法
y = x! + 1; // 通过非空断言,告诉编译器 x不为 null

3.空值合并运算符

空值合并二元运算符?? 用于检查左侧表达式的求值是否等于null。如果是,则表达式的结果为右侧表达式;否则,结果为左侧表达式。

换句话说,a ?? b等价于三元运算符a != null ? a : b。

在以下示例中,getNick方法如果设置了昵称,则返回昵称;否则,返回空字符串:

class Person {
  name: string | null = null

  getName(): string {
    // return this.name === null ? '' : this.name
    // 等同于 如果 name不为空 就返回 name 反之返回 ''
    return this.name ?? ''
  }
}

4.可选链

在访问对象属性时,如果该属性是undefined或者null,可选链运算符会返回undefined。

(可选链?当前面的对象不为空或undefined才调用属性或方法)

class Dog {
  bark() {
    console.log('啊呜~')
  }
}

class Person {
  name?: string
  dog?: Dog

  constructor(name: string, dog?: Dog) {
    this.name = name
    this.dog = dog
  }
}

const p: Person = new Person('jack')

// p.dog.bark()// 报错 dog 可能为空

// 逻辑判断
if (p.dog) {
  p.dog.bark()
}

// 当 dog不为null 或 undefined 时 再调用 bark 并且不会报错
p.dog?.bark()

4.模块化🎈

  1. 把一个大的程序拆分成【互相依赖】的若干小文件
  2. 这些小文件还可以通过【特定的语法】组合到一起
  3. 这个过程称之为【模块化】

优点:

1.更好维护

2.更好的复用性

缺点:

需要学习模块化语法

分析:

  1. 功能写完只有10行代码,模块化没啥必要!
  2. 功能写完有100行,或者1000行代码,里面有【好几段逻辑】在其他地方也要用--模块化

1.默认导入与导出🏃‍♂️

ArkTS 中每个 ets 文件都可以看做是一个模块,默认只能在当前文件中使用,如果要在其他位置中使用就需要:

  1. 当前模块中 导出模块

  2. 需要使用的地方 导入模块

    // 默认导出 
    export default 需要导出的内容
    
    // 默认导入
    import xxx from '模块路径'
    
    //试一试
    // Model.ets
    // 一起写
    export default class Person {
      name: string = ''
    }
    
    // 分开写
    // export default Person
    
    // 只能有一个默认导出
    
    // TestModel.ets
    import Person from './model'
    
    const p: Person = new Person()
    
    

    路径

    / 表示进入某个文件夹里面
    
    .  表示当前文件所在文件夹                  ./
    
    .. 表示当前文件的上一级文件夹             ../
    
    

2.按需导入与导出🏃‍♂️

如果有很多的东西需要导出,可以使用按需导出,他也有配套的导入语法

// ---------- Model.ets ----------
const info: string = '信息'

// 单独写
export const num: number = 10

function sayHi() {
  console.log('你好吗~')
}

// 或者 写到 {} 内部
export {
  info, sayHi
}

// ---------- MainFile.ets ----------
import { info, num, sayHi as sayHello } from './Model'

console.log('info:', info)
// 起别名
sayHello()
console.log('num:', num + '')

3.全部导入🏃‍♂️

导出部分不需要调整,调整导入的语法即可

import * as Utils from './utils'
// 通过 Utils 即可获取 utils模块中导出的所有内容

5.定时器🎈

日常开发中如果需要代码 延迟一会执行,或者每隔一段时间执行一次,就可以使用定时器

定时器有两种:

  1. setTimeout: 延迟执行
  2. setInterval: 间隔执行

1.setTimeout(延迟执行)🏃‍♂️

setTimeout可以用来设置一个定时器,当设置的时间到了之后就会去执行指定的函数

执行一次用 setTimeout

setTimeout、clearTimeout是一个全局函数,可以直接使用

@Entry
@Component
struct Page01_timeoutAndInterval {
  timeId?: number

  build() {
    Column({ space: 20 }) {
      Text('延时器-Timeout')
        .fontSize(30)
        .width('100%')
        .textAlign(TextAlign.Center)
      Row({ space: 20 }) {
        Button('开启延时器')
          .onClick(() => {
            // 开启延时器
            let timeId = setTimeout(() => {
              // 延迟执行逻辑
              console.log('代码执行啦')
            }, 2000)

            //  保存定时器 id
            this.timeId = timeId
          })
        Button('关闭延时器')
          .onClick(() => {
            // 调用 clearTimeout 清除定时器
            clearTimeout(this.timeId)
          })
      }
    }
    .padding(10)
  }
}

2.setInterval(间隔执行)🏃‍♂️

setInterval 也是可以用来设置一个定时器,根据设置的时间间隔来执行指定的函数

执行多次用 setInterval!!

setInterval、clearInterval是一个全局函数,可以直接使用

// 1. 基本使用
setInterval(()=>{
  // 延迟执行的逻辑
},时间)

// 2. 获取返回的定时器 id
const timeId = setInterval(()=>{
  // 延迟执行的逻辑
},时间)

// 3. 根据定时器 id 清除定时器
clearInterval(timeId)
//试一试
@Entry
@Component
struct Page02_interval{
  // 只是用来保存数据,不影响 UI 可以不加状态修饰符
  timeId?: number

  build() {
    Column({ space: 20 }) {
      Text('定时器-Interval')
        .fontSize(30)
        .width('100%')
        .textAlign(TextAlign.Center)
      Row({ space: 20 }) {
        Button('开启延时器')
          .onClick(() => {
            this.timeId = setInterval(() => {
              console.log('代码执行啦~')
            }, 2000)
          })
        Button('关闭延时器')
          .onClick(() => {
            clearInterval(this.timeId)
          })
      }
    }
    .padding(10)
  }
}

6.递归🎈

调用自身的函数我们称之为递归函数

基本用法

在某种意义上说,递归近似于循环。两者都重复执行相同的代码,并且两者都需要一个终止条件(避免无限循环,或者在这种情况下更确切地说是无限递归)。

// 没有退出条件的 递归函数--无限递归
function func(){
  func()
}
//试一试
let num: number = 1

function printLog(num: number) {
  console.log(`你好,第${num}次打印`)
  // 递减
  num--
  // 退出条件
  if (num > 1) {
    printLog(num)
  }
}

// 打印 10 次
printLog(10)

---------------------------------------感觉写的不错的话就点点关注吧😘❤❤❤----------------------------------------------