从今天起,我要开始学习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);
总结:何时定义接口
- 定义对象的格式:描述数据模型,api响应格式,配置对象等等,是开发中用的最多的场景
- 类的契约:规定一个类需要实现那些方法和属性
- 自动合并:一般用于扩展第三方库的方法和类型,大型项目中会用到
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();