3.ArkTS的进阶语法
1.类
1.概述
类是用于创建对象的模板,根据这个模版,我们可以使用new关键字创建出很多具有不同属性值和行为的对象
作用:在鸿蒙开发中,我们可以使用类来进行对象的创建
例如:const date = new Date()
date.getFullYear()
// 类名 首字母大写(规范)
class 类名{
// 属性
// 方法
// 构造函数
}
// 使用类 实例化对象 基于类 创建对象
const x:类名 = new 类名()
new关键字做了什么事情:
- 创建一个新对象
- 函数内部的this指向这个新对象
- 执行构造函数代码
- 返回新对象
2.全面理解类
1.实例属性
通过实例属性来保存各种类型的数据
// 类
class 类名{
// 字段名+类型+初始值
字段名:类型='xxx'
// 可选字段可以不设置初始值
字段名?:类型
}
// 可选字段在使用时需要配合 可选链操作符 避免出错
试一试:
- 定义类 Person
- 字段:
-
- name,字符串,默认为 ''
- food,字符串,可选
- 实例化 Person 对象,并赋值name 属性
- 依次打印 name,food 的 length 属性
/*
* 类:作用:根据一个模板动态创建对象
* 类定义语法:
* class 类名 {
* 属性
* 方法
* }
*
* 使用类创建一个对象:
* let obj:类名 = new 类名()
* */
// 需求:创建一个杨幂对象,名字和年龄
// 1. 定义类
class YmPerson {
name: string = '杨幂'
age: number = 20
}
// 2. 使用类创建对象
let ymObj: YmPerson = new YmPerson()
console.log(ymObj.name, ymObj.age)
let ymObj1: YmPerson = new YmPerson()
console.log(ymObj1.name, ymObj1.age)
@Entry
@Component
struct Index {
// sc = new Scroller()
// listsc = new ListScroller()
build() {
Column() {
}
.onClick(() => {
// this.listsc.scrollToItemInGroup(0)
})
.height('100%')
.width('100%')
}
}
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%')
}
}
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')
4. 静态属性、方法
类还可以添加静态属性、方法,后续访问需要通过 类 来完成,例如:Array.from 传送门
// 定义
class 类{
static 字段:类型
static 方法(){}
}
// 使用
类.字段
类.方法()
/*
* 静态属性和方法语法结构:
* 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%')
}
}
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%')
}
}
6.super 关键字
子类通过 super 可以访问父类的实例字段、实例方法和构造函数。可以在适当的时候使用
class 父类 {
func(){
}
}
class 子类 extends 父类 {
constructor() {
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%')
}
}
7.instanceof
instanceof 运算符可以用来检测某个对象是否是某个类的实例
试一试:
1定义父类
2定义子类,继承父类
3实例化子类 并通过 instanceof 进行判断
4判断 数组 是否为 Array 的实例
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)
8.修饰符
类的方法和属性可以通过修饰符来 限制访问
修饰符包括:readonly、private、protected和public。省略不写默认为 public
1.readonly(只读)
readonly 的意思是只读,可以用来修饰属性(字段),修饰之后外部只可以取值,无法修改
class 类{
readonly 属性:类型
}
2.private(私有)
private修饰的成员不能在声明该成员的类之外访问,包括子类
class 类{
private 属性:类型
private 方法(){}
}
3.protected(受保护的)
protected修饰符的作用与private修饰符非常相似,不同点是protected修饰的成员允许在派生类(子类)中访问
class 父类{
protected 属性:类型
protected 方法(){}
}
class 子类 extends 父类{
方法(){
// 可以访问父类 protect 修饰的 属性、方法
}
}
4.public(公共)
public修饰的类成员(字段、方法、构造函数)在程序的任何可访问该类的地方都是可见的。
class 类{
public 属性
public 方法(){}
}
readonly | 只读 | 属性 | 无限制 |
private | 私有 | 属性、方法 | 类内部可以访问 |
protected | 保护 | 属性、方法 | 类及子类可以访问 |
public | 公共 | 属性、方法 | 无限制 |
2.接口
1.接口继承
接口继承使用的关键字是 extends
interface 接口1{
属性1:类型
}
interface 接口2 extends 接口1 {
属性2:类型
}
试一试:
- 定义接口 1
- 定义接口 2 继承接口 2
- 使用接口 2 进行类型限制
interface IPerson {
name: string
}
interface IStudent extends IPerson {
studentNo: string
}
const student: IStudent = {
name: '张三',
studentNo: 'A20241028001'
}
2.接口实现(类来实现接口)
可以通过接口结合 implements 来限制 类 必须要有某些属性和方法,
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()
// }
// }
(使用 implements 可以实现多态,即一个接口对象可以有多种实现形式)
3. 泛型
泛型在保证类型安全(不丢失类型信息)的同时,可以让函数等与多种不同的类型一起工作,灵活可复用
通俗一点就是:类型是可变的!
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'])
类型变量可以用在任意支持的位置,实现更为复杂的功能
//将类型变量 Type,作为数组项的类型即可
function identity<Type>(arr: Type[]): number {
return arr.length
}
/*
* 泛型函数演示:
* 1. 泛型函数语法:
* function 函数名称<T>(形参:T) {
* let tmp:T = 形参
* return [形参] // T[]
* }
*
* ✨✨总结:泛型函数的使用步骤
* 1. 泛型函数定义(类型使用T来占位)
* 2. 泛型函数调用(传入具体类型)
* */
2.使用泛型约束
如果开发中不希望任意的类型都可以传递给 类型参数 ,就可以通过泛型约束来完成
核心步骤:
- 定义用来约束的 接口(interface)
- 类型参数通过 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.多个泛型参数
日常开发的时候,如果有需要可以添加多个 类型变量,只需要定义并使用 多个类型变量即可
/*
* 泛型函数多个参数以及多个参数的类型约束
* 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)
}
}
4.泛型接口
定义接口时结合泛型,那么这个接口就是 泛型接口
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'] // ❌
}
5.泛型类
和泛型接口类似,如果定义类的时候结合泛型,那么这个类就是 泛型类
// 原始定义一个类
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)