type、typeof、类、剩余和展开、简单和复杂类型

五. type、typeof、类、剩余和展开、简单和复杂类型

今日核心:
ArkTS 语法进阶:type、typeof、类、剩余和展开、简单和复杂类型

1. ArkTS-回顾

在继续深入学习ArkTS之前先回顾一下核心的概念

  1. ArkTS和 TS 以及 JS 的关系
    a. JS: JavaScript,常用于网页开发,页面的效果
    b. TS: TypeScript,微软开发的,比 JS 多了类型系统
    c. ArkTS: JS、TS能用的,他 基本 都能用,写 UI
    d. 注意:看文档的时候,可能需要去 JS 的文档,TS 的文档找一部分的内容

在这里插入图片描述
2. 声明式 UI 开发范式
在这里插入图片描述

  1. 组件语法
容器组件(参数) {
  子组件....
}
	.属性1()
	.属性2()
	.属性N()

普通组件(参数)
	.属性1()
	.属性2()
	.属性N()

2. 类型别名

类型别名,顾名思义就是给某个类型起别名,之后就可以通过这个别名来使用类型啦。咱们开发中用到的一些内置类型就是通过 type 来定义的哦

type 别名 = 类型
// 后续在使用类型时 直接使用【别名】即可

试一试:

  1. 使用 类型别名 保存联合类型
  2. 直接通过类型别名来使用这个联合类型
  3. 看看 ResourceColor 和 ResourceStr 是如何定义的!
// 定义类型别名
type IDType = string | number

// 使用类型别名
function printID(id:IDType ) {
  console.log(id+'')
}

// 调用函数
printID(10)
printID('20')

@Entry
@Component
struct Page01_type {
  @State message: string = 'Type类型别名';

  build() {
    Row() {
      Column() {
        Text(this.message)
          .fontSize(30)
          .fontWeight(FontWeight.Bold)
      }
      .width('100%')
    }
    .height('100%')
  }
}

3. typeof运算符

可以通过 typeof 运算符来获取类型,他返回的是一个字符串

// 后面跟上需要获取类型的 数据或变量 即可
typeof 表达式

试一试:

  1. 依次获取 number、string、boolean、undefined、function 的类型
  2. 获取 对象、数组、null的类型
// 前面 5 个可以正常获取到类型
console.log(typeof 123) // number
console.log(typeof '123') // string
console.log(typeof false) // boolean
console.log(typeof undefined) // undefined

function func() {
}

console.log(typeof func) // function

interface Person{
  name:string
}

// 对象 数组 null 获取到的都是 object
const p: Person = {name:'jack'}
console.log(typeof null) // object
console.log(typeof [1, 2, 3]) // object
console.log(typeof p) // object

@Entry
@Component
struct Page02_typeof {
  @State message: string = 'typeof';

  build() {
    Row() {
      Column() {
        Text(this.message)
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
      }
      .width('100%')
    }
    .height('100%')
  }
}

4. 类

类是用于创建对象的模板。他们用代码封装数据以处理该数据。同时类声明也会引入一个新类型,并定义其字段、方法和构造函数。

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


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

4.1. 实例属性

通过实例属性(字段)来保存各种类型的数据

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

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


试一试:

  1. 定义类 Person
  2. 字段:
    a. name,字符串,默认为 ‘’
    b. food,字符串,可选
  3. 实例化 Person 对象,并赋值name 属性
  4. 依次打印 name,food 的 length 属性
class Person {
  name: string = 'jack'
  food?: string
}

const p = new Person()
p.name = 'jack'

console.log(p.name)
console.log('', p.food?.length)

4.2. 构造函数

上一节的代码实在 实例化 之后,挨个对属性进行赋值,如果自定义了构造函数,可以在构造函数中完成该操作

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

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

试一试:

  1. 定义类
    a. 添加多个字段,不设置默认值
  2. 构造函数接收参数,内部完成字段的 初始化 操作
  3. 使用接口类型简化参数传递
  4. 描述一下
interface IFood {
  name: string
  price: number
}

class Food {
  name: string
  price: number

  // constructor(name:string,price:number) {
  //   this.name=name
  //   this.price = price
  // }
  constructor(value: IFood) {
    this.name = value.name
    this.price = value.price
  }
}

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

现在再来看看当初创建控制器的写法,并且跳进去看看定义:const sc = new Scroller()

4.3. 实例方法

类中可以定义 方法,并且在内部编写逻辑.这种方法需要通过实例化的对象调用-称之为实例方法

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

试一试:

  1. 定义类
  2. 添加字段,在构造函数中 进行初始化
  3. 添加方法,内部通过 this 获取实例对象并使用
  4. 描述如下代码的意义
    a. const ls = new Scroller()
    b. ls.scrollToIndex(1)
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.4. 静态属性、方法

类还可以添加静态属性、方法,后续访问需要通过 类 来完成

// 定义
class 类{
  static 字段:类型
  static 方法(){}
}

// 使用.字段
类.方法()

试一试

  1. 定义类
  2. 添加静态属性、静态方法
  3. 使用定义的 静态属性、方法
  4. 跳转到 TransitionEffect.move() 的定义查看是什么方法?
class Person{
  static staticField:string ='静态字段'
  static staticMethod(){
    console.log('静态方法')
  }
}
Person.staticField
Person.staticMethod()

4.5. 继承

类可以通过 继承 快速获取另外一个类的 字段 和 方法

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

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

父类:也可以叫做 基类 、超类 等
子类:也可以叫做 派生类、继承类 等

试一试:

  1. 定义父类,添加属性(字段)、方法、构造函数
  2. 定义子类,继承父类
  3. 实例化子类,看看能跟欧访问父类的 属性、方法、构造函数
  4. 子类重写父类同名方法,测试调用
  5. 看看组件的继承关系,比如Row,
    a. 看看 Row 方法的返回值的继承关系?
    b. 看看通用属性是定义在哪里的?
class Person {
  name: string
  age: number

   sayHi() {
    console.log(`你好,我叫:${this.name}`)
  }

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

// 通过 extends 继承父类
class Student extends Person {
  // 子类 可以添加自己的属性 和 字段
  height: number = 170

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

  // 子类可以重写同名字段 、方法
  sayHi(): void {
    console.log('子类的 sayHi')
  }
}

const s: Student = new Student('jack', 18)
// 直接可以使用 继承而来的 属性 方法 构造函数
s.name
s.sayHi()

4.6. super 关键字

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

class 父类 {
  func(){
    
  }
}

class 子类 extends 父类 {

  constructor() {
    super() // 调用父类构造函数
  }
  方法(){
    super.方法() // 调用父类方法
  }
}

试一试:

  1. 定义父类,添加属性、方法,构造函数
  2. 定义子类、
    a. 添加属性、
    b. 构造函数、内部通过 super 调用父类构造函数完成父类属性的初始化
    c. 添加方法、内部通过 super 调用父类方法,复用逻辑
class Person {
  name: string
  age: number

  sayHi() {
    console.log(`你好,我叫:${this.name}`)
  }

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

class Student extends Person {
  height: number

  constructor(name: string, age: number, height: number) {
    // name 和 age 的初始化通过 super 来调用父类的构造函数
    super(name, age)
    this.height = height
  }

  sayHi(): void {
    // 通过 super 访问父类的 字段 方法
    super.sayHi()
    super.name
    super.age
    console.log('子类的 sayHi')
  }
}

const s: Student = new Student('jack', 18, 170)

4.7. instanceof

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

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

试一试:

  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)

4.8. 修饰符

类的方法和属性可以通过修饰符来 限制访问
修饰符包括:readonly、private、protected和public。省略不写默认为 public

4.8.1. readonly(只读)

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

class 类{
  readonly 属性:类型
}

试一试

  1. 定义类
    a. 添加属性(字段)
    b. 挑选一个设置 readonly
  2. 实例化对象,尝试 读写 readonly 修饰的属性
  3. 看看 Math.pi 是如何定义的
4.8.2. private(私有)

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

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

试一试:

  1. 定义类
    a. 添加属性(字段)、方法
    b. 通过 private 修饰
  2. 类内部访问 private 修饰的内容
  3. 类外部访问 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()
4.8.3. protected(受保护)

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

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

试一试:

  1. 定义父类
    a. 添加属性、方法
    b. 通过 protected、private 修饰
  2. 定义子类
    a. 内部访问父类中通过protected、private 修饰的内容
// 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+'') // 无法访问 报错
  }
}
4.8.4. public(公共)

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

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

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

class Person {
  public name: string = ''
  // 省略 等同于使用 public
  age: number = 0

  public sayHi(){
    console.log(`你好,我叫:${this.name}`)
  }
}

// public 公有
const p = new Person()

// 使用 public 修饰的字段和方法,通过对象直接可以访问
p.name
p.age
p.sayHi()
修饰符名作用适用范围
readonly只读属性
private私有属性、方法
protect保护属性、方法
public公共属性、方法

4.9. 案例-根据描述实现类

接下来咱们来一起根据描述来实现几个具体的类,来巩固一下刚刚学习的语法

编写一个名为 Vehicle 的基类,表示交通工具:

  1. 属性:
    ○ id: 交通工具唯一标识符(字符串类型),要求在创建对象时必填。
    ○ color: 交通工具颜色(字符串类型),默认值为 ‘未知’。
  2. 方法:
    ○ setColor(color: string): 设置交通工具颜色,返回一个字符串描述新的颜色设置情况: ‘颜色设置为 xxx’。
  3. 静态方法:
    ● generateID(prefix: string): 生成一个标识符(字符串)并返回。该方法接受一个前缀字符串参数,并返回一个格式为 标示符+4位随机数 的唯一标识符,比如传入 car,返回car9854

基于 Vehicle 类创建两个子类:
● Car 类:继承自 Vehicle 类,表示汽车。汽车除了继承自 Vehicle 类的属性和方法外,还具有以下属性和方法:
○ 属性:
■ make: 汽车品牌(字符串类型),要求在创建对象时必填。
■ model: 汽车型号(字符串类型),要求在创建对象时必填。
○ 方法:
■ startEngine(): 启动汽车引擎。返回一个字符串描述汽车信息+引擎启动情况,例如 “比亚迪秦 启动~”。
● Bike 类:继承自 Vehicle 类,表示自行车。自行车除了继承自 Vehicle 类的属性和方法外,还具有以下属性:
○ 属性:
■ frameMaterial: 自行车车架材质(字符串类型),要求在创建对象时必填。

// 基类 Vehicle
class Vehicle {
  id:string //标示符
  color:string='未知' // 颜色
  constructor( id: string,  color?: string ) {
    this.id=id
    if(color!=undefined){
      this.color=color
    }
  }

  setColor(color: string): string {
    this.color = color;
    return `颜色设置为 ${color}`;
  }

  static generateID(prefix: string): string {
    const randomSuffix = Math.floor(Math.random() * 10000)
    return `${prefix}${randomSuffix}`;
  }
}

// 子类 Car
class Car extends Vehicle {
  make:string// 品牌
  model:string // 型号
  constructor(
    id: string,
    color: string,
    make: string,
    model: string
  ) {
    super(id, color);
    this.make=make
    this.model=model
  }

  startEngine(): string {
    return `${this.make} ${this.model} 启动~`;
  }
}

// 子类 Bike
class Bike extends Vehicle {
  frameMaterial:string // 材质
  constructor(id: string, color: string,  frameMaterial: string) {
    super(id, color);
    this.frameMaterial=frameMaterial
  }
}

// 示例用法
const carId = Vehicle.generateID('car');
const myCar = new Car(carId, '蓝色', '比亚迪', '秦');
console.log(myCar.startEngine()); // 输出类似 "比亚迪秦 启动~"

const bikeId = Vehicle.generateID('bike');
const myBike = new Bike(bikeId, '红色', '铝合金');
@Entry
  @Component
  struct Page05_ClassDemo {
    @State message: string = 'class的练习题';

    build() {
      Row() {
        Column() {
          Text(this.message)
            .fontSize(50)
            .fontWeight(FontWeight.Bold)
        }
        .width('100%')
      }
      .height('100%')
    }
  }

5. 剩余和展开

接下来学习一个运算符 …
…在不同的情况下有不同的效果,分别是 剩余参数 和 展开

5.1. 剩余参数

通过剩余参数的语法,我们可以将 函数 或 方法 中一个不定数量的参数表示为一个数组

// 剩余参数 【只能写在最后一位】
function 函数名(参数1,参数2,...剩余参数数组){
  // 逻辑代码
  // 剩余参数之前的参数 挨个获取即可
  // 剩余参数:以数组的形式获取
}

试一试:

  1. 定义累加函数
    a. 默认有 2个参数
    b. 可以在 2 个参数以外再传递任意个参数
    c. 类型均为 number
  2. 返回累加的结果
function sum(numA:number,numB:number,...theArgs:number[]) {
  let total = numA+numbB;
  for (const arg of theArgs) {
    total += arg;
  }
  return total;
}

console.log(sum(1, 2, 3).toString()) // 6

console.log(sum(1, 2, 3, 4).toString()) // 10

5.2. 展开

出于程序稳定性,以及运行性能考虑,在 ArkTS 中 …(展开运算符) 只能用在数组上

注:TS中 … 数组和对象均可以使用

试一试:

  1. 数组合并
  2. 数组的 push 方法结合…运算符,顺便确认 push 方法的参数
    示例代码:
    日常开发中,用来展开数组,常用于用数组合并 及 传递参数时
const numArr1: number[] = [1, 2, 3, 4]
const numArr2: number[] = [5, 6, 7]

// 合并到一起
const totalArr: number[] = [...numArr1, ...numArr2]

// 添加
const numArr3:number[] = [8,9,10]
const numArr4:number[] = [11,12,13]

// 将 numArr4 展开,传递给push
numArr3.push(...numArr4)

6. 简单类型和复杂类型

ArkTS中的数据类型整体可以分为 两大类:

  1. 基本数据类型(简单数据类型)
    number 数字型、string 字符串型、boolean布尔型、undefined未定义、null空类型
  2. 引用数据类型(复杂数据类型)
    Object、Function、Array
    这两类数据在内存中保存的方式略有不同,导致在日常开发中对这两类数据进行赋值操作时会有不同的结果
    比如如下代码:
  3. num1和 num2 的值分别是?
  4. p1.name和p2.name 分别是?
// 基本数据类型
let numA: number = 10
let numB: number = numA
numB++
console.log('numA:', numA) // ?
console.log('numB:', numB) // ?


// 引用数据类型
class Person {
  name: string = ''

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

const p1: Person = new Person('jack')
const p2: Person = p1
// 修改 P2 是否会报错
p2.name = 'rose'

console.log('p1.name:', p1.name) // ?
console.log('p2.name:', p2.name) // ?

6.1. 内存中堆栈空间

咱们可以把内存理解为有 2 类空间:

  1. 栈:访问速度快,基本数据类型存放到栈里面
  2. 堆:存储容量大,引用数据类型存放到堆里面
    在这里插入图片描述

基本数据类型和复杂数据类型是如何保存的呢?

6.1.1. 基本数据类型存储

变量的数据直接存放在栈空间中,访问速度快
在这里插入图片描述

6.1.2. 引用数据类型存储
  1. 栈空间:存放 变量的内存地址(堆中的地址)
  2. 堆空间:存放 变量的 值

在这里插入图片描述
思考:

  1. 通过索引,修改上图中 numArr 某一项的值,改的是哪里的数据?

6.2. 站在内存角度看变量赋值

6.2.1. 基本数据类型

num1 num2 都保存在堆内存中,虽然值相同,但是各自是独立的空间,后续操作 互不影响

let num1: number = 10
let num2: number = num1

在这里插入图片描述

6.2.2. 引用数据类型

p1 和 p2 栈内存中有各自独立的空间,但是保存的是堆内存的地址,指向的是同一个数据:
修改 p2.name ,p1.name 也会受到影响,因为是同一份数据

class Person {
  name: string = ''

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

const p1: Person = new Person('jack')
const p2: Person = p1
p2.name = 'rose'

在这里插入图片描述

鸿蒙应用开发系列课(一)课程相关资料:在第一节课的资料附件里!鸿蒙应用开发系列课程重点一览! 课程亮点- 全面性:覆盖从基础语法到高级架构设计的全方位知识。-  实践性:通过实际编码练习,加深对知识点的理解应用。- 深入性:深入探讨类型系统,强化代码质量开发效率。- 创新性:专注于ArkTS特性,引领鸿蒙开发新趋势。- 实用性:教授实用编程技巧,应对真实世界开发挑战。 课程详细介绍 1. 基础入门- ArkTSTS概述:了解两种语言的起源、特点应用场景。- 开发环境搭建:指导如何配置开发工具环境。 2. 编程基础- HelloWorld程序:编写你的第一个TS程序,开启编程之旅。- 类型系统入门:掌握TS的基础类型类型声明。 3. 深入理解- 类型推断与声明:学习如何让代码更加健壮可维护。- 类型兼容规则:理解TS的类型系统如何确保代码的正确性。  4. 高级特性- 函数与:深入学习函数式编程面向对象编程在TS中的应用。- 泛型与枚举:掌握泛型编程,提升代码的通用性灵活性。 5. 模块化开发- 模块化编程:学习如何通过模块化管理大型项目。- 模块导入导出:掌握模块的导入导出机制,实现代码的解耦。 6. 鸿蒙特性- ArkTS特性:探索ArkTS为鸿蒙应用开发带来的特有优势。- 从TS到ArkTS:学习如何将现有TS代码迁移适配到ArkTS。 7. 持续学习- 新动态:跟进鸿蒙TS的zui新发展更新。-  职业发展:提供职业发展指导,帮助学员规划技术路线。 适合对象本课程适合以下人群:- 编程初学者,希望建立扎实的编程基础。- 有一定编程经验,想深入学习TSArkTS的开发者。- 对鸿蒙应用开发感兴趣的技术爱好者。 课程成果完成本课程后,你将能够:- 学习了解现代软件开发的zui佳实践。- 对TSArkTS有深入的理解实际应用能力。- 鸿蒙学习入门,职场提升个人竞争力,抓住鸿蒙红利风口期!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值