• 【TS】class类和接口


    class可以用来做数据的存储与回显,能将页面的数据分离出来并提取到class内,函数也可以抽离到class,实例化class进行调用。ts中的class类与jsclass类基本相同,不同点在于tsclass有数据类型约束,在使用class的时候,必须遵循定义的数据类型约束,class中有一个constructor,它可以更改class的属性值,实例化class进行传值的时候,传入的值也必须符合constructor参数的数据类型规定。


    class的使用

    class Student {
        // 定义属性
        name : string  = ''
        age : number = 0
        gender : string = ''
        
        // 定义构造函数:为了将来实例化对象的时候,可以直接对属性的值进行初始化
        constructor(name : string,age :number ,gender : string){
            this.name = name
            this.age = age
            this.gender = gender
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    这里的定义属性是实例对象class自身的属性,而不是定义在对象原型上的,在实例化class的时候可以直接获取到class的属性,例如:let student = new Student ,取值:student .name ,此时取值肯定是一个空,因为这个name并没有进行赋值,而是引用了默认值。

    赋值操作

    let {name,age,gender} = { name : '张三', age : 18, gender : "男" }
    let student = new Student(name,age,gender)
    console.log(student)  // 打印 name : '张三', age : 18, gender : "男" 
    
    • 1
    • 2
    • 3

    这里new Student就是实例化class,并将值传入class进行赋值操作,赋值操作是在constructor中完成的,通过new关键词实例化对象时,会自动调用constructor构造函数,tsconstructor在接参的时候,需要定义参数的数据类型,这里传入了name,age,gender,那么在接参的时候,也必须定义这三个值,并且规定的数据类型必须一致。
    this.name = name就是class的实例属性 = 传入的name参数,这一步就是赋值操作。赋值之后就可以在let student这个位置拿到数据。
    直接new class,如果不传值或者传一个null则会使用class内的默认值,相当于是重置class的值;如果new传入值,那么就以传入值为准

    在使用calss时候的注意事项
    1、class的实例属性需要有默认值,如果没有初始化赋值则会报错

    class PersonTest0 {
         // 没有默认值 报错
         name : string
     }
     
    class PersonTest1 {
        name : string
        constructor(info : any){
            // 此处做了一个判断,并不是直接赋值,也会报错,去掉if直接this.name = info.name则不会报错
              if(info){
                this.name = info.name
              }
        }
    }
    
    // 定义了constructor但是没有初始化赋值,报错
     class test2 { 
         name : string
     constructor(name : string ){
         // 没有this.name = name 报错
         }
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    2、class可以不用完整传入定义的属性,传一部分也可以,未传入的以默认值或者undefined展示(ts中需要定义传入类型,未传入的用?可选符声明)。

    interface IPersonTest {
        name : string
        age : number
        address ?: string
    }
    
    class PersonTest {
        name : string = ''
        age : number = 0
        address ?: string = ''
        constructor(info : IPersonTest | null) {
            if(info){
                this.name = info.name
                this.age = info.age
                this.address = info.address
            }
        }
    }
    
    let PInfo = {
        name : 'zs',
        age : 10,
        // address : '湖北'
    }
    let getPerson = new PersonTest(PInfo)  // 传入constructor值,给实例属性赋值
    // 打印 zs , 10 , undefined
    
    • 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

    3、传入的顺序并不影响对类的赋值

    let PInfo = {
        name : 'zs',
        age : 10,
        address : '湖北'
    }
    let PInfo2 = {
        name : '东方不败',
        age : 10,
        address : '湖南'
    }
    
    let getPerson = new PersonTest(PInfo)  // 传入constructor值,给实例属性赋值
    console.log(getPerson);
    console.log(new PersonTest(null)); // 传入constructor值为空则初始化实例属性的值
    console.log(new PersonTest(PInfo2));  // 传入的顺序并不影响对类的赋值,此处传入的是PInfo2,得到的就是PInfo2的值
    console.log(new PersonTest(PInfo));   // 传入的顺序并不影响对类的赋值,此处传入的是PInfo,得到的就是PInfo的值
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在这里插入图片描述


    class内定义方法

    class Student {
        name : string 
        age : number
        gender : string
    
        constructor(name : string ,age :number ,gender : string ){
            this.name = name
            this.age = age
            this.gender = gender
        }
    
        // 定义实例方法
        sayHi(say : string){
            console.log(`${say}你好,我叫${this.name},今年已经${this.age}岁了,我是个${this.gender}孩子`);
        }
    }
    
    let {name,age,gender} = { name : '张三', age : 18, gender : "男" }
    let student = new Student(name,age,gender)
    
    console.log(student.sayHi('你叫什么名字啊?'));
    // 打印 你叫什么名字啊?你好,我叫张三,今年已经18岁了,我是个男孩子
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    此处在class内部定义了一个方法,定义方法跟正常写函数是一样的,有参数且可以设置默认参数,参数必须的定义数据类型,class的实例方法取值的话,用this.xxx就可以获取到calss内的属性值。class的方法直接用实例对象.方法名(student.sayHi())即可。
    sayHi()这个方法其实是直接定义在Student.prototype上面的,因此在类的实例上调用方法,其实是调用原型上的方法


    默认值

    class的属性和方法都可以有默认值,如果实例化class没有传入参数则使用默认值

    
    class cla {
        name : string = '属性默认值'
    }
    let data = new cla ()   // new cla 直接获取到name的值
    console.log(data)  // 打印 属性默认值
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    class cla2 {
        name : string
        constructor(name : string = '东方不败') {
            this.name = name
        }
    }
    let data = new cla2()   // new cla 后 constructor的name赋值给this.name
    console.log(data.name);  // 打印 东方不败
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    class cla3 {
        name : string
        constructor(name : string = '不败') {
            this.name = name
        }
        sayHi(say : string = '东方'){
            console.log(`${say},${this.name}`);
        }
    }
    let data = new cla3() // new cla后,函数获取name的值加上函数自身的默认值
    console.log(data.sayHi());  // 打印 东方,不败
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    静态方法

    
    class openInfo {
        
      fun(list) {  // 实例方法
         console.log(list)
      }
        
      static fun2(list) {  // 静态方法
         console.log(list)
      }
    
    }
    
    let list1 = 50
    new openInfo().fun(list1)   // 打印50
    // class的实例属性需要new ,语法:类名().方法名()
    
    
    let list2 = 100
    openInfo.fun2(list2)   // 打印100
    // class的静态方法不需要new ,语法:类名.方法()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    类相当于实例的原型,所有在类中定义的方法,都不会被实例继承。如果在一个方法的前面加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这个就被称为静态方法。

    静态属性

    通常情况下,类的属性值两种,第一种实例属性,第二种静态属性

    第一种: 实例属性&实例对象原型

    • 实例属性就是定义在类constructor上面,最顶层的位置,不需要this,跟类内部的函数是平级的,这种方式定义的是实例对象自身的属性
    • 而在constructor内定义的this.xxx则为实例对象的原型属性。class可以不用完整传入定义的属性,传一部分也可以,未传入的以默认值或者undefined展示(ts中需要定义传入类型,未传入的用?可选符声明)。
    • 这两个方法并没有什么本质的区别,实例属性上定义的可以有默认值。

    第二种:静态属性&静态方法

    • 在实例属性或者实例方法的前面加一个 static ,则为静态属性,静态属性不需要new,取值方法:类名.属性名 即可

    实例属性是需要 new 的,例如 new Person() 或者 new Person().方法名() 或者 new Person().属性名
    静态属性则直接 类名.属性名,例如 : Person.方法名() 或者 Person.属性名

    class PersonStatic {
        // name在静态属性中是一个关键字,不允许使用
        //static names : string  // 此处不赋值就是一个 undefined
        static names : string = ''
        static age : number = 0
        static address : string = ''
        static obj : any = {
            user : '南瓜粥',
            money : 5
        }
        static sayHi(){}  // 静态方法
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    console.log(new PersonStatic);  // 打印PersonStatic类
    console.log(PersonStatic.names,PersonStatic.age,PersonStatic.address); // 获取初始化的值,空则不显示
    console.log(PersonStatic.names = '东方求败',PersonStatic.age = 10,PersonStatic.address = '湖北');  // 静态属性改值
    console.log(PersonStatic.names = '艺术概论');  // 静态属性改值
    console.log(PersonStatic.obj = {user:'紫薯粥',money:6});  // 对象改值
    // console.log(PersonStatic = 100);  // 报错
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    打印结果:
    在这里插入图片描述

    静态属性可以直接改值,改值的顺序并不影响它的输出,它与实例属性不同的是,实例属性定义的是实例对象自身的属性,是在class身上的。
    在这里插入图片描述
    constructor可以更改实例属性的值,静态属性方法则是直接定义在构造函数constructor中的,所以可以直接改值。而且此处也解释了为什么在静态属性中name无法使用,因为name被类自身占用了。

    在这里插入图片描述

    • 不同点
    • class实例属性可以通过构造函数constructor传入的值来批量的改值
    • 静态属性必须单条的 类名.属性名 改值

    class继承

    class可以使用extends继承父类,如果子类继承父类,那么子类的constructor中,子类的构造函数必须执行一次super()函数。super()作为函数调用时,代表父类的构造函数,子类构造函数中的super()代表调用父类的构造函数,这是必须的,否则报错,并且在super()中传入的参数,相当于给父类的constructor中传入参数。

     class Category {
           constructor(name){
              if(name){
                 this.name = name
              }
           }
           getName = function(){
                console.log('category:'+this.name);
           }
     }
    
    class Drawer extends Category {
         constructor(info){
              super(info.name)
                 if(info){
                       this.age = info.age
                 }   
         }
    }
    const data = new Drawer({name:'东方不败',age:100})
    console.log(data);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    在这里插入图片描述
    并且,多个对象生成对象实例都是独立的,不会产生影响。

    const data = new Drawer({name:'东方不败',age:100})
    const data2 = new Drawer({name:'西方求败',age:200})
    data.name = '艺术概论'
    console.log(data);
    console.log(data2);
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述


    接口

    1、接口可以约束class内部值的数据类型以及方法,class使用接口之后,接口定义的数据类型和方法必须全部真正实现,并且在实现的基础上,class可以有自己的属性和方法。接口内可以定义属性和方法,这里以方法为例子。

    // 接口
    interface IFly{
        fly() : string  // 方法 返回字符串类型
    }
    
    
    class Person implements IFly { 
        fly() : string {    // 实现接口中的方法,class没有fly()会报错
            return '我会飞了'
        }
        
        run() : string {  // calss自身定义的方法
            return '我会跑了'
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    Person类里必须得有 IFly 定义的 fly(),而且方法得有字符串返回值 ,否则报错。

    2、class类可以实现一个接口也可以实现多个接口,接口中的内容都要真正实现

    // IMy 继承上面定义的两个接口
    interface IRun{
        run() : string // 方法 返回字符串类型
    }
    interface IFly{
        fly() : string  // 方法 返回字符串类型
    }
    
    //  类可以实现一个接口也可以实现多个接口,接口中的内容都要真正实现
    class Person2 implements IFly,IRun {
        fly() : string {
            return '我会飞了'
        }
        run() : string {
            return '我会跑了'
        }
    }
    
    let person2 = new Person2()
    console.log(person2.fly(),person2.run());
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    接口是可以继承的,class可以使用继承的接口,也可以使用多个接口,方法是一样的,关于接口继承,请看另外一篇: 【TS】接口和接口继承


    案例源码:https://gitee.com/wang_fan_w/ts-seminar

    如果觉得这篇文章对你有帮助,欢迎点亮一下star

  • 相关阅读:
    [基础服务] CentOS 7.x 安装NodeJS环境并搭建Hexo
    面向对象编程(C++篇4)——RAII
    取得PMP证书需要多长时间?
    Android学习之路(19) ListView详解
    前端对多对时间段进行处理 取并集合并没有交集的项
    欠拟合与过拟合
    java健身房管理系统演示录像2021计算机毕业设计MyBatis+系统+LW文档+源码+调试部署
    大数据HBase学习圣经:一本书实现HBase学习自由
    在微服务架构架构中父工程中的`<dependencyManagement>`和 `<dependencies>`的区别
    去除做题痕迹的软件有哪些?安利三款好用的
  • 原文地址:https://blog.csdn.net/qq_44793507/article/details/127875687