学习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();
相关推荐
超哥--13 小时前
B站视频内容智能分析系统(九):React 前端与管理面板
前端·react.js·前端框架
Cutecat_16 小时前
视频字幕处理工具横向:提取模式 vs 编辑模式,该如何选择
android·前端·ios·语音识别
qq_4221525716 小时前
PDF 加水印工具怎么选?2026 年文档版权保护方案对比
前端·pdf·github
kyriewen16 小时前
手写 Promise.all、race、any:不到 30 行代码,解决并发异步的所有姿势
前端·javascript·面试
brucelee18617 小时前
OpenClaw 浏览器控制(Chrome MCP)完整教程
前端·chrome
ct97817 小时前
React 状态管理方案深度对比
开发语言·前端·react
胡志辉的博客18 小时前
深入浅出理解浏览器事件循环:从一道输出题讲到 Chrome 源码
前端·javascript·chrome·chromium·event loop
代码不加糖18 小时前
js中不会冒泡的事件有哪些?
前端·javascript·vue.js
退休倒计时18 小时前
【每日一题】LeetCode 53. 最大子数组和 TypeScript
数据结构·算法·leetcode·typescript
懂懂tty18 小时前
Vue2与Vue3之间API差异
前端·javascript·vue.js