学习ts笔记(二):属性修饰符,泛型,接口

从今天起,我要开始学习React了,本篇说一些React基本概念,算是入门概述,根据B站尚硅谷React教学视频形成的笔记

1.复习类相关知识点

1.1定义class类

ts 复制代码
// 定义一个类
class Person {
  name: string;
  age: number;
  //  定义一个属性
  constructor(name:string,age:number) {
    this.name = name;
    this.age = age;
  }
  // 定义一个方法
  speak() {
    console.log(`我的名字${this.name},我的年龄${this.age}`);
  }
} 
// 创建实例对象
const p = new Person('张三', 20);
p.speak();

1.2类的继承

ts 复制代码
// 定义一个类
class Person {
  name: string;
  age: number;
  //  定义一个属性
  constructor(name:string,age:number) {
    this.name = name;
    this.age = age;
  }
  // 定义一个方法
  speak() {
    console.log(`我的名字${this.name},我的年龄${this.age}`);
  }
} 
// 类的继承
class Student extends Person {
  grade: number;
  constructor(name:string,age:number,grade:number) {
    super(name,age);
    this.grade = grade;
  }
  // 重写父类的方法-使用关键字override:可以检查父类是否有这个方法,如果没有就会报错
  override speak() {
    console.log(`我的名字${this.name},我的年龄${this.age},我的年级${this.grade}`);
  }

}
// 创建实例对象
const p = new Student('张三', 20, 3);
p.speak();

2.属性修饰符

修饰符总结:

public-公开的-可以被类的内部,类外部,子类访问

protected-受保护的-可以被:类的内部,子类访问

private-私有的-可以被:类的内部访问

readonly-只读属性-属性无法被修改

2.1public修饰符

ts 复制代码
// 定义一个类
class Person {
  // 属性和方法默认修饰符是public,可以省略
  name: string;
  age: number;
  //  定义一个属性
  constructor(name:string,age:number) {
    this.name = name;
    this.age = age;
  }
  // 定义一个方法
  speak() {
    //此处验证public修饰符,可以在类的内部访问属性和方法name和age
    console.log(`我的名字${this.name},我的年龄${this.age}`);
  }
}
class Student extends Person {
  // 定义一个属性
  lg() {
    //此处验证:public修饰符,可以在子类中访问父类的属性和方法name和age
    console.log(`我的名字${this.name},我的年龄${this.age}`);
  }
}
let stu = new Student('张三', 20);
//此处验证:public修饰符,可以在类的外部访问属性和方法name和age
stu.speak()
stu.lg()
stu.name
stu.age

2.2属性的简写形式

ts 复制代码
//属性的简写形式-简写前
class Person {
  public name: string;
  public age: number;
  //  定义一个属性
  constructor(name:string,age:number) {
    this.name = name;
    this.age = age;
  }
}
// 属性的简写形式-简写后
class Person1 { 
  constructor(public name:string,public age:number) {}
}

2.3 protected修饰符

ts 复制代码
class Person{
  constructor(
    private name:string,
    public age:number,
    protected sex:string,
  ){

  }
  speak(){
    //验证:protected修饰的属性能在类的内部访问
    console.log(`${this.name} 说话了 ,他${this.age},${this.sex}`);
  }
}
class Student extends Person{
  sp() {
    //验证:protected修饰的属性能在子类访问
    //此处访问-name报错 ,但是age,sex合法
    console.log(`${this.name} 学习了 ,他${this.age},${this.sex}`);
  }
}
const p = new Person('张三', 20, '男');
p.speak();
//验证:protected修饰的属性不能在类的外部访问
// console.log(p.age); --- IGNORE --- 
// console.log(p.sex); --- IGNORE --- 

2.4 private修饰符

ts 复制代码
class Person{
  constructor(
    private name:string,
    public age:number){

  }
  speak(){
    //验证:private修饰的属性能在类的内部访问
    console.log(`${this.name} 说话了 ,他${this.age}`);
  }
}
class Student extends Person{
  sp() {
    //验证:private修饰的属性不能在子类访问
    //此处访问-name报错 ,但是age合法
    console.log(`${this.name} 学习了 ,他${this.age}`);
  }
}
const p = new Person('张三', 20);
p.speak();
//验证:private修饰的属性不能在类的外部访问
// console.log(p.age); --- IGNORE --- 

2.5 readonly修饰符

ts 复制代码
class Person{
  constructor(
    public name:string,
    public readonly sex:string,
  ){

  }
  speak(){
    //验证:protected修饰的属性能在类的内部访问
    console.log(`${this.name} 说话了 ,他${this.age},${this.sex}`);
  }
}
const p1 = new Person('zhasang','男');
p1.name = 'litang'
//当sex变成readonly之后,就不能修改了
//p1.sex = '女' //报错:无法分配到

3.抽象类

概述:抽象类是一种无法被实例化的类,专门用来定义类的结构和行为,类中可以写抽象方法,也可以写具体方法,抽象类主要用来为其派生类提供一个基础结构,要求派生类必须实现其中的抽象方法 简述:抽象类不能实例化,其存在意义是可以被继承,抽象类里边可以有普通方法,也可以有抽象方法

通过以下场景,理解一下抽象类: 我们定义一个抽象类package,表示所有包裹的基本结构,任何包裹都有重量属性weight,包裹都需要计算运费属性,不同包裹计算运费属性的方式不同,因此用于计算运费的calculate方法是一个抽象方法,具体实现由子类实现

ts 复制代码
// abstract-定义一个抽象类的关键字
abstract class Package{
  constructor(public weight:number) {

  }
  // 抽象类
  abstract calculate():number
  // 具体类
  consoleMeth() {
    console.log(`这时候一个包裹${this.weight}kg,运费是${this.calculate()}元`)
  }
}
class AirPackage extends Package{
  calculate():number {
    return this.weight * 10
  }
}

class TrainPackage extends Package{
  constructor(weight:number,public height:number) {
    super(weight)
  }
  calculate(): number {
    return this.weight * 5 + this.height * 2
  }

}
const air = new AirPackage(5)
const train = new TrainPackage(5, 10)
air.consoleMeth()
train.consoleMeth()

总结:何时使用抽象类

1.定义通用接口:为一组相关的类定义通用的行为(方法和属性)

2.提供基础实现:在抽象类中,提供某些方法或为其提供基础实现,这样派生类就可以继承这些实现了

3.确保关键实现,强制派生类实现一些关键行为

4.共享代码和逻辑:当多个类需要共享部分代码时,抽象类可以避免代码重复

4.接口interface

interface是一种定义结构的方式,主要作用是为:类,方法,函数等定义一种契约,这样可以确定代码的一致性和类型安全,但要注意interface只能定义格式,不能包含任何实现

4.1定义类的结构

ts 复制代码
// 定义一个接口
interface PersonInterface {
  name:string
  age:number
  speak(n:number ):void
}
class Person implements PersonInterface {
  constructor(public name:string,public age:number){

  }
  speak(n:number):void {
    console.log(`我叫${this.name},今年${this.age}岁了,我说了${n}句话`)
  }
}
const p1 = new Person('张三',18)
p1.speak(5)

4.2定义对象的结构

ts 复制代码
// 定义一个对象接口
interface PersonInterface {
  name:string
  readonly age:number //只读属性age
  sex?:string    //sex为可选属性
  speak(n:number ):void
}
const p1:PersonInterface = {
  name:'张三',
  age:18,
  speak(n:number):void{
    console.log(`我说了${n}句话`)
  }
}
const p2:PersonInterface = {
  name:'张三',
  age:18,
  sex:'男',
  speak(n:number):void{
    console.log(`我说了${n}句话`)
  }
}
// p1.age = 20 //报错,age是只读属性
p1.speak(5) //我说了5句话
p2.sex = '女' 

4.3定义函数的结构

ts 复制代码
interface FunctionInterface {
  (x:number,y:string):string
}
const countStr:FunctionInterface = (x,y) => {
  return `${x} ${y}`  
}
console.log(countStr(2,'hello'))

4.4接口之间的继承

ts 复制代码
// 接口之间的继承
interface PersonInterface {
  name:string
  age:number
}
interface StudentInterface extends PersonInterface {
  grade:number
}
class student implements StudentInterface {
  constructor(public name:string,public age:number,public grade:number) {
 
  }

}
let s1 = new student('张三',18,90)
console.log(s1)

const sstudent1:StudentInterface ={
  name:'李四',
  age:20,
  grade:95
}
console.log(sstudent1)

4.5接口自动合并(可重复定义)

ts 复制代码
interface PersonInterface {
  name: string;
  age: number;
}
// 重复定义接口
interface PersonInterface {
  gender: string;
}
// 接口会合并重复定义的属性,最终的接口包含了所有属性
const person: PersonInterface = {
  name: "张三",
  age: 18,
  gender: "男",
};
console.log(person);

总结:何时定义接口

  1. 定义对象的格式:描述数据模型,api响应格式,配置对象等等,是开发中用的最多的场景
  2. 类的契约:规定一个类需要实现那些方法和属性
  3. 自动合并:一般用于扩展第三方库的方法和类型,大型项目中会用到

5.一些相似概念的区别

5.1 interface和type的区别

  • 相同点:interface和type都可以定义对象结构,俩者在许多场景中可以互换
  • 不同点:
    1.interface:更专注定义类和对象的结构,支持继承和合并
    2.type:可以定义类型别名,联合类型,交叉类型,但不支持继承和自动合并

5.1.1 interface和type都可以定义对象结构

ts 复制代码
// 使用interface定义对象结构
interface PersonInterface {
  name: string,
  age: number,
  speak(n:string):void
}
// type定义对象结构
type PersonType = {
  sex: string,
  grander: string,
  speak(n:string):void
}
  

// 接口会合并重复定义的属性,最终的接口包含了所有属性
const person: PersonInterface = {
  name: "张三",
  age: 18,
  speak(n:string){
    console.log(n);
  }
};
console.log(person);
const person2: PersonType = { 
  sex:"男",
  grander:"二年级",
  speak(n:string){
    console.log(n);
  }
};
console.log(person2); 

5.1.2 interface可以继承和合并

ts 复制代码
// 使用interface定义对象结构
interface PersonInterface {
  name: string,
  age: number,
  speak(n:string):void
}
//interface可以继承其他接口,使用extends关键字
interface StudentInterface extends PersonInterface {
  sex: string
}
//interface也可以重复定义,重复定义的接口会合并属性
interface StudentInterface {
  grander: string
}
  
const person: StudentInterface = {
  name: "张三",
  age: 18,
  sex: "男",
  grander: "男",
  speak(n:string){
    console.log(n);
  }
};
console.log(person);

5.1.3 type的交叉类型

ts 复制代码
// 使用interface定义对象结构
interface PersonInterface {
  name: string,
  age: number,
  speak(n:string):void
}
//interface可以继承其他接口,使用extends关键字
interface StudentInterface extends PersonInterface {
  sex: string
}
//interface也可以重复定义,重复定义的接口会合并属性
interface StudentInterface {
  grander: string
}
//使用interface定义对象结构结束

//使用type定义对象结构
//使用type的&符号实现接口的合并
type PersonType = {
  name: string,
  age:number
}& {
  speak(n:number):void
}
//使用&符号实现接口的继承
type StudentType = PersonType & {
  sex: string
}

const person1: StudentType = {
name: "李四",
age: 20,  
speak(n:number){
  console.log(n); 
},
sex: "女"
}

console.log(person1);
const person: StudentInterface = {
  name: "张三",
  age: 18,
  sex: "男",
  grander: "男",
  speak(n:string){
    console.log(n);
  }
};
console.log(person);

5.2 interface和抽象类的区别

相同点:都用来定义一个类的结构(应该遵守的契约)

不同点:

1.接口interface:只能描述结构 ,不能有任何实现代码 ,一个类可以实现多个 接口

2.抽象类:既可以包含抽象方法 ,也可以包含具体方法 ,一个类只能继承一个抽象类

5.2.1 一个类可以实现多个接口

ts 复制代码
// 使用interface定义对象结构
interface SwimInterface {
  swim(n:string):void
}
interface FlyInterface {
  fly(n:string):void
}
class Duck implements SwimInterface, FlyInterface {
  swim(n:string): void {
    console.log(`鸭子游泳${n}米`)
  }
  fly(n:string): void {
    console.log(`鸭子飞行${n}米`)
  } 
}
const duck = new Duck()
duck.swim('100')
duck.fly('1000')  

//一个对象也可以同时满足多个接口的要求
const Duck1:SwimInterface & FlyInterface = {
  swim(n:string): void {
    console.log(`鸭子游泳${n}米`)
  },
  fly(n:string): void {
    console.log(`鸭子飞行${n}米`)
  }
}
Duck1.swim('100')
Duck1.fly('1000')

6.泛型

泛型允许我们在定义函数,类,接口时,使用类型参数来表示未指定的类型 ,这些参数在具体使用时 ,才被指定具体的类型,泛型可以让同一段代码适用于多种类型,同时仍然保持类型的安全性 如下代码中就是泛型,不一定非叫T,设置泛型后即可在函数中使用T来表示该类型

6.1泛型函数

ts 复制代码
function da<T> (data:T) {
  console.log(data);
}
da<string>('123')
da<number>(123)

6.2泛型可以有多个

ts 复制代码
// :T|U用来限制函数的返回值只能是传入的两种类型之一
function da<T,U> (data:T,data2:U):T|U {
  console.log(data);
  return Date.now() % 2?data:data2
}
da<string,Boolean>('123',true)
da<number,number>(123, 8)

6.3泛型接口

ts 复制代码
interface Person<t> {
  name: string;
  age: number;
  info: t;
}
const p1: Person<string> = {
  name: "张三",
  age: 20,
  info: "这是一个人",
};
const p2: Person<number> = {
  name: "李四",
  age: 25,
  info: 123,
};
type infoType = {
  q: string;
  w: number;
}
const p3: Person<infoType> = {
  name: "王五",
  age: 30,
  info: {
    q: "这是一个人",
    w: 456,
  },
};
console.log(p1);
console.log(p2);
console.log(p3);

6.4泛型约束

ts 复制代码
interface PersonInterface {
    name: string;
    age: number;
  
}
function getPersonInfo<t extends PersonInterface>(p:t):void {
    console.log(p.name);
    console.log(p.age);
}
getPersonInfo({name:'张三',age:20});

6.5泛型类

ts 复制代码
class Person<T>{
  constructor(
    public name:string,
    public age:number,
    public info:T 
  ){}
  sayHi(){
    console.log(`大家好,我是${this.name},今年${this.age}岁了!,${this.info}`);
  }
}
// 测试1
const p1 = new Person<number>('张三', 18, 20);
console.log(p1);
p1.sayHi();
// 测试2
type InfoType ={
  a:string,
  b:number
}
const p2 = new Person<InfoType>('李四', 20, {a:'hello',b:123});
console.log(p2);
p2.sayHi();
相关推荐
明月_清风4 小时前
全面了解 Vercel:前端开发者的高效武器库与实战指南
前端·next.js
NiceCloud喜云4 小时前
Claude API PDF 文档问答实战:从原生解析到分页引用的完整方案
java·服务器·前端·网络·数据库·人工智能·pdf
东方小月4 小时前
vibecoding实战:用 Claude Code 从0到1开发一个 Claude Code
前端·人工智能·架构
marsh02064 小时前
54 openclaw钩子函数使用:在框架生命周期中注入自定义逻辑
java·前端·spring
TechExplorer3654 小时前
npm install 日志目录
前端·npm·node.js
笔优站长5 小时前
从 Vue 2 到 Vue 3:我把 vue-aliplayer-v2 重构成了一个更现代的阿里云播放器组件
前端·vue.js
蓝银草同学5 小时前
新手指南:快速理清独立仓库 Java 8 多模块项目依赖并运行
前端·后端
蓝银草同学5 小时前
前端转 Java,第一篇看懂 pom.xml:Maven 依赖管理从入门到不懵
前端·后端
彦为君5 小时前
JavaSE-11-网络编程(详细版)
java·前端·网络·ai·ai编程