• 【typescript】面向对象(下篇),包含接口,属性的封装,泛型


    假期第八篇,对于基础的知识点,我感觉自己还是很薄弱的。
    趁着假期,再去复习一遍

    面向对象:程序中所有的操作都需要通过对象来完成

    计算机程序的本质就是对现实事物的抽象,抽象的反义词是具体。比如照片是对一个具体的人的抽象,汽车模型是对具体汽车的抽象等。

    在程序中所有的对象被分成两个部分,数据和功能。以人为例,人的姓名,性别,年龄,身高,体重等属于数据,人可以走路,说话,吃饭,睡觉这些属于人的功能,数据在对象中被称为属性,而功能称之为方法

    1、接口

    接口与类型声明很相似,类型声明使用type关键字定义,接口(Interface)使用Interface关键字定义

    类型声明可以描述一个对象的类型,对对象进行限制,可以用来定义基本类型、联合类型、交叉类型、元组类型等

    type myType = {
        name: string,
        age:number
    }
    
    const obj: myType = {
        name: '莲花',
        age:38
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    类型声明,相同的名只能声明一次

    在这里插入图片描述

    接口同样可以描述一个对象的类型,对对象进行限制

    接口也可以当作类型声明去使用,接口也是用来定义一个类结构,用来定义一个类中应包含哪些属性和方法,多了少了都会波浪线提示

    interface myInterface {
        name: string,
        age:number
    }
    
    const obj: myInterface = {
        name: '莲花',
        age:38
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    但是接口可以重复声明
    在这里插入图片描述
    接口主要用于描述对象的结构和类型,因此更适用于被实现或继承的情况。
    接口可以在定义类的时候去限制类的结构
    接口中的所有属性都不能有实际的值
    接口只定义对象的结构,而不考虑实际的值
    在接口中所有方法都是抽象方法

    interface Person {
      name: string;
      age: number;
      sayHello(): void;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    接口支持一些类型声明不支持的特性,例如可选属性、只读属性、函数类型等。

    interface Person {
      name: string;
      age?: number; // 可选属性
      readonly id: number; // 只读属性
      sayHello(): void; // 函数类型
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    类型声明不支持定义可选属性、只读属性等,但支持联合类型、交叉类型等高级类型的定义,比接口更加灵活。

    type Person = {
      name: string;
      age: number;
    } & {
      gender: 'male' | 'female'; // 交叉类型
    } | {
      gender?: string; // 联合类型
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    类型声明主要用于定义类型别名和复杂类型

    type Age = number;
    
    type Person = {
      name: string;
      age: Age;
    };
    
    type Gender = 'male' | 'female';
    
    type PersonInfo = {
      name: string;
      age: Age;
      gender: Gender;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2、属性的封装

    为什么需要属性的封装?当通过class定义类,通过实例对象可以随意修改属性,对于一些特定的属性,比如钱,比如年龄,都不应该被随意的修改,属性如果可以任意被修改,将导致对象中的数据变得非常不安全

    
    class Person  {
        name: string;
        age: number;
        
        constructor(name: string, age: number){ 
            this.name = name,
            this.age = age
        }
    
    }
    
    const person1 = new Person('莲花', 18)
    person1.name = '楼楼'
    person1.age = -38
    console.log(person1, 'person1')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    很显然年龄不应该是负数,但是因为对象属性可以任意修改,如果有需要用到年龄做计算的地方,一定是会出错的。
    那如果不希望被随意修改,可以在属性前添加属性的修饰符
    在这里插入图片描述
    访问修饰符包括三种:

    public:公共的,可以在类的内部和外部访问,默认值为 public。
    private:私有的,只能在类的内部访问(修改)。
    protected:受保护的,只能在类的内部和派生类中访问。

    class Person {
      private name: string;
      protected age: number;
      public gender: string;
    
      constructor(name: string, age: number, gender: string) {
        this.name = name;
        this.age = age;
        this.gender = gender;
      }
    
      public sayHello() {
        console.log(`Hello, my name is ${this.name}, I'm ${this.age} years old.`);
      }
    }
    
    class Student extends Person {
      public grade: number;
    
      constructor(name: string, age: number, gender: string, grade: number) {
        super(name, age, gender);
        this.grade = grade;
      }
    
      public study() {
        console.log(`${this.name} is studying in grade ${this.grade}.`);
      }
    }
    
    const person = new Person('Alice', 18, 'female');
    console.log(person.name); // 编译错误,name 是 private 属性,只能在类的内部访问
    console.log(person.age); // 编译错误,age 是 protected 属性,只能在类的内部和派生类中访问
    console.log(person.gender); // 可以访问,gender 是 public 属性
    person.sayHello(); // 可以访问,sayHello 是 public 方法
    
    const student = new Student('Bob', 16, 'male', 10);
    console.log(student.name); // 编译错误,name 是 private 属性,只能在类的内部访问
    console.log(student.age); // 编译错误,age 是 protected 属性,只能在类的内部和派生类中访问
    console.log(student.gender); // 可以访问,gender 是 public 属性
    student.sayHello(); // 可以访问,sayHello 是 public 方法
    console.log(student.grade); // 可以访问,grade 是 public 属性
    student.study(); // 可以访问,study 是 public 方法
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    如果想获取Person 中定义为私有属性的name,可以通过定义方法,向外暴露属性和修改属性,虽然同样可以修改,但是属性的访问权是内部定的,暴露出来才可以访问,不暴露就是私有属性

    class Person {
        private name: string;
        protected age: number;
        public gender: string;
    
        constructor(name: string, age: number, gender: string) {
            this.name = name;
            this.age = age;
            this.gender = gender;
        }
    
        public sayHello() {
            console.log(`Hello, my name is ${this.name}, I'm ${this.age} years old.`);
        }
        //定义方法,用来获取name属性
        getName() {
            return this.name
        }
        //定义方法,用来设置name属性,从外面传入value
        setName(value:string) { 
         this.name = value
        }
    }
    
    const person = new Person('Alice', 18, 'female');
    person.getName(); // 可以访问,间接的拿到name属性
    person.setName('张丫丫')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    上面提到的年龄不可以设置负数,也就可以通过方法进行限制
    getter方法用来读取属性,setter方法用来设置属性,它们被称为属性的存取器

          getAge() { 
            return this.age
           }
        setAge(value: number) {
            //先判断年龄是否合法
            if (value >= 0) { 
              return this.age = value
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    3、泛型

    泛型是一种在编译时还不确定具体类型的变量或函数。使用泛型可以增加代码的灵活性和可重用性,使代码更加通用化

    假如我们知道参数的类型,方法的返回值类型,可以这样写:

    function fn(a: number): number { 
        return a 
    }
    fn(1)
    
    • 1
    • 2
    • 3
    • 4

    假如有些情况下,不知道参数和返回的具体类型是什么,类型可以写成any,确实可以,但是any实际上会关闭掉ts中的类型检查
    参数是any,返回值是any,就有可能参数是字符串,返回值是数值

    function fn(a: any): any{ 
        return a 
    }
    fn(1)
    
    • 1
    • 2
    • 3
    • 4

    所以在定义函数或类的时候,类型不明确就可以使用泛型
    在方法名后面用尖括号,大写的T或者任意大写字母都可以
    在这里插入图片描述
    在这里插入图片描述
    泛型T只有在函数执行的时候才知道具体是什么类型
    两种方法调用:

    function fn<T>(a: T): T { 
    
        return a 
    }
    
    // 方法一:直接调用,不知道泛型,TS也可以自动对类型进行推断
    fn(1)
    // 方法二:手动指定泛型,如果有很复杂的情况,类型推断无法推断的时候
    fn<string>('1')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    泛型可以指定多个

    function fn2<T,K>(a: T,b:K): T { 
    console.log(b);
    
        return a 
    }
    
    fn2(10,'嘻嘻嘻')
    或
    fn2 <number,string>(10,'嘻嘻嘻')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    如果想限制泛型的范围,定义一个接口,希望泛型是实现Inter接口的类

    //T extends Inter 表示泛型必须是Inter的实现类(子类)

    传参的时候要有length属性,传数字没有会提示类型’number’的参数不能赋值给类型’Inter’的参数,因为数字里没有length
    在这里插入图片描述
    传字符串或有length属性的对象就可以
    在这里插入图片描述
    如果有双层尖括号< > 的语法通常用于表示嵌套的泛型参数

    export type pageResult<T> = {
      item:T[]
    }
    export type GuessItem = {
      desc:string,
      discount:string,
      price:number
    }
    
    export const getHomeAPI=()=>{
      return http<pageResult<GuessItem>>({
        method:'GET',
        url:''
      })
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    首先,定义了两个类型别名:

    pageResult:代表一个拥有 item 属性的对象,其中 item 是一个数组,数组元素的类型由泛型参数 T 决定。
    GuessItem:代表一个具体的类型,包含了 desc、discount 和 price 属性。

    在 getHomeAPI 函数中,使用了 http 函数来发起网络请求。该函数返回一个 Promise,Promise 的泛型类型是 pageResult< GuessItem>,也就是一个 pageResult 类型的对象,其中 item 数组的元素类型为 GuessItem

    通过这种方式,双层尖括号 < > 提供了一种在函数签名或类型别名中指定嵌套泛型参数的机制。在上述示例中,getHomeAPI 函数的返回类型中的双层尖括号指定了 pageResult 类型的泛型参数为 GuessItem 类型,从而定义了函数返回值的类型

  • 相关阅读:
    CentosLinux 7 字符安装教程
    python根据多边形polygon生成掩膜图像问题cv2.fillPoly()和cv2.fillConvexPoly()
    【博学谷学习记录】超强总结,用心分享|架构师-设计模式 1
    READ TABLE
    如何使用 JMeter 进行 HTTPS 请求测试?
    Centos7部署Python3环境
    可靠性工程师的发展之路
    std::copy代替memcpy
    QCC51XX---Pydbg应用
    Spring MVC(建立连接 + 请求)
  • 原文地址:https://blog.csdn.net/weixin_49668076/article/details/133577846