1.类

类是用于创建对象的模板,根据这个模版,我们可以使用new关键字创建出很多具有不同属性值和行为的对象

作用:在鸿蒙开发中,我们可以使用类来进行对象的创建

// 类名 首字母大写(规范)
class 类名{
  // 属性
  
  // 方法
  
  // 构造函数
}


// 使用类 实例化对象 基于类 创建对象
const x:类名 = new 类名()

new关键字做了什么事情:

  1. 创建一个新对象
  2. 函数内部的this指向这个新对象
  3. 执行构造函数代码
  4. 返回新对象

1.1实例属性

在 ArkTS 中,你可以在类的内部定义实例属性。实例属性属于类的每个实例,不同的实例可以有不同的属性值。

// 类
class 类名{
  // 字段名+类型+初始值
  字段名:类型='xxx'
  // 可选字段可以不设置初始值
  字段名?:类型
}

// 可选字段在使用时需要配合 可选链操作符 避免出错

1.2构造函数

构造函数是类中的一个特殊方法,用于在创建类的实例时初始化对象的状态。

写法:

class 类{
  字段A:类型
  字段B:类型

  constructor(参数...) {
    // 通过 new 实例化的时候 会调用 constructor
    // 通过关键字 this 可以获取到实例对象
  }
}
const 实例 = new 类()

举个列子

/*
 构造函数的作用: 一个类中一旦有了显式编写 constructor, 在new 类()的时候是可以传参的
 * 这个参数是可以给类中的属性设置初始值,我们可以通过new 时传入不同的数据类决定创建什么阳的对象
 *
 * ✨✨注意点:
 * 1. 如果一个类中没有写constructor构造函数,类本身是有一个默认的没有带参数的构造函数
 * 2. 如果想要在new的时候带参数,则我们在定义类的时候,应该要显式编写constructor,参数类型和数量由我们自己定
 *
 * new 和构造函数的关系
 * ① 调new 的时候开辟一个内存空间 -> 写上类中的属性 此时没值
 * ② 调用构造函数给属性赋值
 * ③ 将创建好的对象返回交给我们定义的变量
 * */

// 需求:创建一个杨幂对象,名字和年龄
// 1. 定义类
class Person {
  name: string
  age: number

  // 在构造函数中可以给属性做初始化值操作
  constructor(pname: string, page: number) {
    // 将 参数赋值给 类中的属性(字段)
    this.name = pname
    this.age = page
  }
}

// 2. 使用类创建对象
let ym: Person = new Person('杨幂', 20)
console.log(ym.name,ym.age)

let lxx:Person = new Person('刘诗诗',19)
console.log(lxx.name,lxx.age)

@Entry
@Component
struct Index {
  // sc = new Scroller()
  // listsc = new ListScroller()

  build() {
    Column() {

    }
    .onClick(() => {
      // this.listsc.scrollToItemInGroup(0)
    })
    .height('100%')
    .width('100%')

  }
}

另外一种写法,传接口类型

interface IFood {
  name: string
  price: number
}

class Food {
  name: string
  price: number

  constructor(value: IFood) {
    this.name = value.name
    this.price = value.price
  }
}

const f = new Food({ name: '花菜炒蛋', price: 10 })

1.3实例方法

实例方法是定义在类中,属于类实例的函数。每个类的实例都可以调用这些方法,它们可以用于执行特定的操作、修改实例的属性等

写法:

class 类名{
  方法名(参数...):返回值类型{
    // 逻辑...
    // 可以通过 this 获取实例对象
  }
}

例子:

class Person{
  name:string

  constructor(name:string) {
    this.name = name
  }

  sayHi(name:string){
    console.log(`你好${name},我的名字是:${this.name}`)
  }
}

const p:Person = new Person('jack')
p.sayHi('rose')

1.4静态属性,方法

在 ArkTS 中,静态属性和静态方法是属于类本身,而不是类的实例的属性和方法。它们可以直接通过类名来访问,不需要创建类的实例。

/*
 * 静态属性和方法语法结构:
 * class 类名 {
 *   static 属性名;属性类型
 *   static 方法名(){}
 * }
 * */

// 定义一个工具类
class Tools {
  // 静态的属性
  static PI:number = 3.1415926
  static getRandom() {
    return Math.random()
  }
}

// 使用
console.log(Tools.PI.toString())
console.log(Tools.getRandom().toString())

@Entry
@Component
struct Index {
  // sc = new Scroller()
  // listsc = new ListScroller()

  build() {
    Column() {

    }
    .onClick(() => {
      // this.listsc.scrollToItemInGroup(0)
    })
    .height('100%')
    .width('100%')

  }
}

1.5继承

类可以通过 继承 快速获取另外一个类的 字段方法。并且还可以扩展属于自己的字段和方法加强父类没有的能力。

写法:

class 父类 {
  // 字段
  // 方法
  // 构造函数
}

class 子类 extends 父类{
  // 自己的字段(属性)
  // 自己的方法
  // 可以重写父类方法
}

父类:也可以叫做 基类 、超类

子类:也可以叫做 派生类、继承类

/*
 * 类的继承:
 * 作用:可以扩展一个类的属性和方法
 * 语法: 子类 extends 父类
 * */

// 1. 父类 - 人
class Person {
  name: string
  age: number

  constructor(name: string, age: number) {
    this.name = name
    this.age = age
  }

  sayHi() {
    console.log('说话')
  }
}

// 2. 子类 - 学生 继承了父类人
class Student extends Person {
  classNo: string = '鸿蒙2期'

  // 由于父类中有一个sayHi,所以Student会覆盖(重写方法)
  sayHi() {
    console.log('普通话 ,合肥方言');
  }

  // 由于这个方法父类中没有,所以是扩展的方法
  Learning(){
    console.log('学习');
  }
}

// 使用
let mm: Student = new Student('明明', 22)
console.log(mm.name, mm.age, mm.classNo)
mm.sayHi()

// let p :Person = new Person('明明', 22)
// p.

@Entry
@Component
struct Index {
  // sc = new Scroller()
  // listsc = new ListScroller()

  build() {
    Column() {

    }
    .onClick(() => {
      // this.listsc.scrollToItemInGroup(0)
    })
    .height('100%')
    .width('100%')

  }
}

1.6 super关键字

子类通过 super 可以访问父类的实例字段、实例方法和构造函数。可以在适当的时候使用

/*
 * super关键字
 * 作用:super是在子类中调用父类中的实例属性,实例方法,构造函数之用的
 * 语法结构:
 * 前置结构,子类 extends 父类
 * super的使用:在子类中通过 super可以调用父类的成员
 *
 * 常用:✨✨通过super类调用父类的构造函数进行属性值的显示调用初始化
 * */

// 1. 父类 - 人
class Person {
  // 实例属性(字段)
  name: string
  age: number

  // 构造函数
  constructor(name: string, age: number) {
    this.name = name
    this.age = age
  }

  // 实例方法
  sayHi() {
    console.log('说话')
  }
}

// 2. 子类 - 学生 继承了父类人
class Student extends Person {
  classesNo: string

  constructor(name: string, age: number, classesNo: string) {
    super(name, age)  // ✨✨super调用父类的构造函数进行父类中的实例属性初始化,只能写在子类的constructor中的第一行
    // super()
    this.classesNo = classesNo
  }

  // 由于这个方法父类中没有学习这个方法,所以是扩展的方法
  Learning() {
    super.sayHi()
    // super.name // 获取到的是undefined 请使用this
    console.log(this.name + '学习')
  }
}

// 类中的this指向的是 new 出来的类的对象实例
let p: Student = new Student('明明', 22,'鸿蒙2期')
console.log(p.name , p.age,p.Learning())

@Entry
@Component
struct Index {
  // sc = new Scroller()
  // listsc = new ListScroller()

  build() {
    Column() {

    }
    .onClick(() => {
      // this.listsc.scrollToItemInGroup(0)
    })
    .height('100%')
    .width('100%')

  }
}

1.7instanceof

instanceof 运算符可以用来检测某个对象是否是某个类的实例

写法:

// 返回判断结果 布尔值
实例对象 instanceof Class



class Person {
  name: string = ''
}

class Student extends Person {
  age: number

  constructor(age: number) {
    super()
    this.age = age
  }
}

const s = new Student(10)
console.log('isStudent', s instanceof Student)
console.log('isPerson', s instanceof Person)
console.log('isArray', [1, 2, 3,] instanceof Array)

const arr = [1, 2, 3]
// 等同于
const arr2 = new Array(1, 2, 3)

1.8 类的修饰符

访问修饰符用于控制类的成员(属性、方法等)的访问权限,readonly、private、protected和public。省略不写默认为 public

1.8.1 readonly(只读)

readonly 的意思是只读,可以用来修饰属性(字段),修饰之后外部只可以取值,无法修改

class 类{
  readonly 属性:类型
}

1.8.2 private(私有)

private修饰的成员不能在声明该成员的类之外访问,包括子类

class 类{
  private 属性:类型
  private 方法(){}
}




class Person {
  // private 设置的时候不嫩省略
  private name: string = ''
  private age: number = 0

  public sayHi() {
    // 内部可以访问
    console.log(`你好,我叫:${this.name}`)
  }
}
class Student extends Person{
  public sayHello() {
    // 内部可以访问
    console.log(`你好,我叫:${super.name}`) // 无法访问 报错
  }
}

const p = new Person()
// p.name // 无法访问 报错
p.sayHi()

1.8.3 protected(受保护的)

protected修饰符的作用与private修饰符非常相似,不同点是protected修饰的成员允许在派生类(子类)中访问

class 父类{
  protected 属性:类型
  protected 方法(){}
}
class 子类 extends 父类{
  方法(){
    // 可以访问父类 protect 修饰的 属性、方法
  }
}






// protect 受保护
class Person {
  protected  name: string = ''
  private age:number=18
  sayHi():void{
    console.log(this.name)
    console.log(this.age+'')
  }
}

class Student extends Person{

  sayHello(){
    console.log(this.name) // 可以访问
    console.log(this.age+'') // 无法访问 报错
  }
}

1.8.4 public(公共的)

public修饰的类成员(字段、方法、构造函数)在程序的任何可访问该类的地方都是可见的。

class 类{
  public 属性
  public 方法(){}
}

默认的修饰符就是 public,外部可以访问

默认不写就是public

总结:

2.接口

它主要用于定义对象的结构,起到约束和规范代码的作用。

2.1接口继承

接口继承使用的关键字是 extends

  1. 定义接口 1
  2. 定义接口 2 继承接口 2
  3. 使用接口 2 进行类型限制

interface IPerson {
  name: string
}

interface IStudent extends IPerson {
  studentNo: string
}

const student: IStudent = {
  name: '张三',
  studentNo: 'A20241028001'
}

2.2接口实现

可以通过接口结合 implements 来限制 类 必须要有某些属性和方法,

interface 接口{
  属性:类型
  方法:方法类型
}

class 类 implements 接口{
  // 必须实现 接口中定义的 属性、方法,否则会报错
}
  1. 定义接口
    1. 定义属性、方法
  1. 定义类
    1. 实现接口
    2. 额外添加 属性 、方法

interface iMath {
  PI: number
  random: () => number
}

class myMath implements iMath {
  PI: number = 3.14

  random() {
    return Math.random()
  }
}

const mmath:iMath = new myMath()


// class myMath1 implements iMath {
//   PI: number = 3.1415926
//
//   random() {
//     return Date.now()
//   }
// }

3.泛型

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

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

为了更好的理解为什么需要 类型可变,咱们来一个具体的例子:一个函数,将传入的参数,转换成数组返回

// 1. 字符串
function getStr(arg: string) {
  return [arg]  // 返回 string[]类型数组
}

// 2. 数字
function getNum(arg: number) {
  return [arg] // 返回 number[]类型数组
}

// 3. 布尔
function getBoolean(arg: boolean) {
  return [arg] // 返回 boolean[]类型数组
}

3.1泛型函数

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

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

  • 可以自行定义,有意义即可
  • 首字母大写
  • 参见泛型参数名 T、Type

// 定义泛型参数 Type,后续可以使用类型的位置均可以使用比如: 形参、函数内部、返回值

// 1. 参数类型
function 函数名<Type>(形参:Type){
}

// 2. 返回值类型
function 函数名<Type>(形参:Type):Type{
}

示例:

/*
 * 泛型函数演示:
 * 1. 泛型函数语法:
 *    function 函数名称<T>(形参:T) {
 *      let tmp:T = 形参
 *      return [形参]   // T[]
 * }
 *
 * ✨✨总结:泛型函数的使用步骤
 * 1. 泛型函数定义(类型使用T来占位)
 * 2. 泛型函数调用(传入具体类型)
 * */

// 需求:传入一个参数,返回这个参数的数组形式
// 1. 定义泛型函数
function getDataList<T>(args: T) {
  return [args] //返回T[]
}

// 2. 调用泛型函数,注意:要传入具体类型
let nums = getDataList<number>(100)
console.log('',nums)

getDataList<string>('100')
getDataList<boolean>(true)
getDataList<string[]>(['100','200'])

@Entry
@Component
struct Index {
  build() {
    Column() {

    }
    .height('100%')
    .width('100%')
    .backgroundColor(Color.Pink)

  }
}

3.2泛型约束

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

核心步骤:

  1. 定义用来约束的类型(可以是联合类型(项目封装会用,我们主要讲这个)、枚举、interface等)
  2. 类型参数通过 extends 即可实现约束

联合类型举例

// 泛型函数getData的T约束只能传入数字和字符串这两个类型的参数
function getData<T extends number | string>(args:T){
  return [args]
}

getData('ok') // ✔️
getData(100) // ✔️
getData(true) // ❌

枚举类型约束

// 约束T只能是Color枚举中的一个值
function getColor<T extends Color>(color: T) {
  return color
}

getColor(Color.White)// ✔️
getColor('White')// ❌

接口类型约束

// 定义接口
interface iPerson {
  name: string
}

// 泛型函数T被iPerson约束
function getData<T extends iPerson>(args: T) {
  return args
}

let obj: iPerson = { name: '张三' }
let arr = [1,2,3]
getData(obj)  // ✔️
getData(arr)  // ❌

3.3多个泛型参数

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

/*
 * 泛型函数多个参数以及多个参数的类型约束
 * 1. 多个参数语法:
 *  函数名<T1,T2,...>(参数1:T1,参数2:T2,...) {}
 *举例:
 function funA<T1, T2>(a1: T1, a2: T2) {
  console.log('a1=', a1, 'a2=', a2)
}
 *
 * 2. 多个参数类型约束语法:
 *   函数名<T1 extends 类型,T2 extends 类型,....>()
 *   函数名<T1 extends 类型,T2 ....>()
 *
 * 类型可以是:基本类型和复杂类型,联合类型,枚举
 举例:
 function funA<T1 extends string, T2 extends number>(a1: T1, a2: T2) {
  console.log('a1=', a1, 'a2=', a2)
}
 *
 * */

function funA<T1 extends string, T2 extends number>(a1: T1, a2: T2) {
  console.log('a1=', a1, 'a2=', a2)
}

funA<string, number>('明明', 22)//✔️
// funA<string, boolean>('明明', true)  //❌


@Entry
@Component
struct Index {
  build() {
    Column() {

    }
    .height('100%')
    .width('100%')
    .backgroundColor(Color.Pink)

  }
}

3.4泛型接口

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

interface 接口<Type>{
  // 内部使用Type
}
interface iData<T> {
  code: number,
  msg: string,
  data: T
}

let obj1: iData<string[]> = {
  code: 200,
  msg: 'ok',
  //data:[1,2]  // ❌
  data:['1','2']  // ✔️
}

let obj2: iData<number[]> = {
  code: 200,
  msg: 'ok',
  data:[1,2]  // ✔️
  //data:['1','2']  // ❌
}

3.5泛型类

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

  1. 定义泛型类,接收泛型参数 T
  2. 实现:
    1. 定义属性,类型为 T
    2. 构造函数,接收属性,类型为T
    3. 方法,返回属性,类型为T
// 原始定义一个类
class Person1 {
  id: number

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

  getId() {
    return this.id
  }
}
// 原始定义一个类
class Person2 {
  id: string

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

  getId() {
    return this.id
  }
}

// 使用泛型类简化
class Person <T> {
  id: T

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

  getId() {
    return this.id
  }
}

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

3.6 类型别名

类型别名,顾名思义就是给某个类型起别名,之后就可以通过这个别名来使用类型啦

基础语法:

type 别名 = 类型
// 后续直接使用别名即可
/*
 * 联合类型语法规则:
 * 作用:根据现有的数据类型来定义一个新的数据类型别名,方便编码
 * 语法:type 类型别名 = 类型
 *
 * 注意点:
 * 1. 类型别名使用的时候初始值设定要和类型别名中的类型保持一致
 * */

// 1. 定义了一个类型别名Length1
type Length1 = number | string

// 2. 使用类型别名定义变量的类型即可
let w1:Length1 = '100%'// ✔️
let w2:Length1 = 100  // ✔️
// let w3:Length1 = true //❌

@Entry
@Component
struct Index {

  build() {
    Column() {

    }
    // .backgroundColor()
    .height('100%')
    .width('100%')

  }
}

4.工具类型

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

4.1 Partial<Type>

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

/*
  *Partial<Type> 作用:可以将一个类型属性转为可选
  * 语法:Partial<类型> 返回新类型 ,这个新类型使用type别名来进行接管
  *
 * */

interface Person {
  name:string
  age:number
}

// Partial<Person>本质上基于原有的Person类型创建一个新类型
type newPerson = Partial<Person>

const p:newPerson = {  }



class Person {
  name: string = ''
  age: number = 0
  friends: string[] = []
}

type ParPerson = Partial<Person>

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

4.2 Required<Type>

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

interface Person {
  name?:string
  age?:number
}

type newPerson = Required<Person>


class Person {
  name?: string
  age?: number
  friends?: string[]
}

type RequiredPerson = Required<Person>

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

4.3 Readonly<Type>

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

class Person {
  name: string = ''
  age: number = 0
}

type ReadonlyIPerson = Readonly<Person>

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

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

4.4 Record<Keys,Type>

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

@State person = {'name':'张三'}  // ❌这样定义报错 -> 因为没有使用interface指定对象类型

// 如果不想定义interface,就可以直接使用Record<Key,Value>来指定对象类型即可
@State person:Record<string,string> = {'name':'张三'}  // ✔️

✨✨注意:{}对象的属性名类型要对应Record<Key类型,value类型>  中的Key类型,属性值对应value类型

适用HarmonyOS NEXT / API12或以上版本 -----------------

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐