zoukankan      html  css  js  c++  java
  • Typescript 实战 --- (5)类

    1、es类 vs ts类
     
    相同点:类成员的属性都是实例属性,而不是原型属性;类成员的方法都是原型方法
    不同点:ts 中类成员的属性必须有初始值,或者是在构造函数中被初始化
    class Dog {
      constructor(name: string) {
        this.name = name;
      }
      name: string;
      run() {}
    }
    
    console.log(Dog.prototype);    // Dog { run: [Function] }
    
    let dog = new Dog('Bob');
    console.log(dog);             // Dog { name: 'Bob' }
    
    // name 属性只在实例上,不在原型上;run 方法只在原型上,不在实例上

    声明一个类的传统写法和简化写法

    // 传统写法
    class Person {
      public name: string;
    
      constructor(name: string) {
        this.name = name;
      }
    }
    
    // 简化写法
    class Animal {
      constructor(public name: string) {}
    }
    class Dog extends Person{
      constructor(public leg: number, public name: string) {
        super(name);
      }
    }
    
    const person = new Person('Bob');
    const dog = new Dog(4, 'XiaoTianQuan');
    2、类的继承
     
    与es一样,ts中类的继承也是通过 extends 关键字,并且,在子类构造函数中,super关键字必须放在第一行
    class Animal {
      constructor(name: string) {
        this.name = name;
      }
      name: string;
    }
    
    class Dog extends Animal {
      constructor(name: string, leg: number) {
        super(name);
        this.leg = leg;
      }
      leg: number;
      intro() {
        console.log(`My name is ${this.name}, I hava ${this.leg} legs.`);
      }
    }
    
    let bob: Dog = new Dog('Bob', 4);
    bob.intro();   // My name is Bob, I hava 4 legs.
    类的成员方法可以直接返回一个 this,这样就可以实现链式调用
    class WorkFlow {
      step1() { 
        console.log('step1...')
        return this 
      }
    
      step2() { 
        console.log('step2...')
        return this 
      }
    }
    
    new WorkFlow().step1().step2()
    
    // step1...
    // step2...
    在继承的时候,this 也可以表现出多态,这里的多态指的是 this 既可以是父类型,也可以是子类型
    class WorkFlow {
      step1() { 
        console.log('step1...')
        return this 
      }
    
      step2() { 
        console.log('step2...')
        return this 
      }
    }
    
    class MyWorkFlow extends WorkFlow {
      next() {
        console.log('next...')
        return this
      }
    }
    
    new MyWorkFlow().next()    // this 指向子类
                    .step1()   // this 指向父类
                    .next()    // this 指向子类
                    .step2()   // this 指向父类
    
    // next...
    // step1...
    // next...
    // step2...
     
    3、成员修饰符
     
    3-1、public
    对所有人可见,在 ts 中,类的所有成员都默认为 public
    class Animal {
      constructor(name: string) {
        this.name = name;
      }
      // 默认是public,可省略
      public name: string;
      public say () {
        console.log('hello... ...')
      }
    }
    
    class Dog extends Animal {
      constructor(name: string) {
        super(name);
      }
    }
    
    let bob: Dog = new Dog('Bob');
    bob.say();   // hello... ...
    3-2、private
     
    只能被类本身调用,不能被类的实例或者子类调用
    class Animal {
      constructor(name: string) {
        this.name = name;
      }
      name: string;
      private say () {
        console.log('hello... ...')
      }
    }
    
    class Dog extends Animal {
      constructor(name: string) {
        super(name);
      }
    }
    
    // private 不能用于实例
    let jorge: Animal = new Animal('Jorge');
    jorge.say(); // Property 'say' is private and only accessible within class 'Animal'.
    
    // private 不能用于子类
    let bob: Dog = new Dog('Bob');
    bob.say();   // Property 'say' is private and only accessible within class 'Animal'.
     
    用于构造函数,则表示这个类既不能实例化,也不能被继承
    class Animal {
      private constructor(name: string) {
        this.name = name;
      }
      name: string;
    }
    
    // Cannot extend a class 'Animal'. Class constructor is marked as private.
    class Dog extends Animal {
      constructor(name: string) {
        super(name);
      }
    }
    
    let a: Animal = new Animal('aaa');
    // Constructor of class 'Animal' is private and only accessible within the class declaration
    3-3、protected
     
    只能被类本身或者子类调用,不能被实例调用
    class Animal {
      constructor(name: string) {
        this.name = name;
      }
      name: string;
      protected say() {
        console.log(`hello ${this.name}`)
      }
    }
    
    class Dog extends Animal {
      constructor(name: string) {
        super(name);
        this.say(); 
      }
    }
    
    let d: Dog = new Dog('ddd');     // hello ddd
    
    let a: Animal = new Animal('aaa');
    a.say();
    // Property 'say' is protected and only accessible within class 'Animal' and its subclasses.
     
    用于构造函数,表示这个类不能被实例化,只能被继承,相当于是声明了一个基类
    class Animal {
      protected constructor(name: string) {
        this.name = name;
      }
      name: string;
    }
    
    class Dog extends Animal {
      constructor(name: string) {
        super(name);
      }
    }
    
    let d: Dog = new Dog('ddd'); 
    
    let a: Animal = new Animal('aaa');
    // Constructor of class 'Animal' is protected and only accessible within the class declaration.
    3-4、readonly
     
    只读属性必须在声明时或构造函数里被初始化,且不可更改
    class Dog {
      constructor(name: string) {
        this.name = name;
      }
      readonly name: string;
      readonly leg: number = 4;
    }
    
    let d: Dog = new Dog('Bob');
    d.name = 'Carl';  // Cannot assign to 'name' because it is a read-only property.
    d.leg = 2;  // Cannot assign to 'leg' because it is a read-only property.
    3-5、static
     
    静态属性,只能通过类名来调用。调用方式是 类名.静态属性名,类的静态成员也可以被继承
    class People {
      constructor(name: string) {
        this.name = name;
      }
      name: string;
      static legs: number = 2; 
    }
    
    class Student extends People {
      constructor(name: string) {
        super(name);
      }
    }
    
    console.log(People.legs);   // 2
    console.log(Student.legs);  // 2
    构造函数的参数也可以添加修饰符,作用是将参数自动变成实例的属性,这样就不用在类中去定义参数了
    class People {
      constructor( public name: string) {
        this.name = name;
      }
      // name: string;   // 标识符“name”重复
    }
    4、getter/setter 存取器
     
    如果一个类没有使用存取器,那么,其成员属性是可以被随意修改的
    class Dog {
      constructor(name: string) {
        this.name = name;
      }
      name: string;
      getName() {
        console.log("name: ", this.name);
      }
    }
    
    let d: Dog = new Dog('Bob');
    d.name = 'Carl';
    d.getName();     // name:  Carl
     
    而封装的基本原则是尽可能的隐藏内部实现细节,只保留一些对外接口使之与外部发生联系,这个时候就需要用到存取器了
    class Dog {
      constructor() {}
      private _name: string;
      get name(): string {
        return this._name;
      }
    
      set name(name: string) {
        if(name.length > 10) {
          console.log('Error: the name is too long!')
        } else {
          this._name = name;
        }
      }
    }
    
    let d: Dog = new Dog();
    
    d.name = 'hello world!';  // Error: the name is too long!
    
    console.log(d.name);   // undefined
    d.name = 'Bob';
    console.log(d.name);   // Bob
    5、抽象类
     
    所谓抽象类,指的是只能被继承,不能被实例化的类
     
    抽象类的好处是可以抽离出一些事物的共性,有利于代码的复用和扩展
     
    使用 abstract 关键字定义抽象类和在抽象类内部定义的抽象方法
    abstract class Animal {}
    
    let a: Animal = new Animal();   // 无法创建抽象类的实例
    // 抽象类可以被继承
    abstract class Animal {
      eat() {
        console.log('eat something... ...')
      }
    }
    
    class Dog extends Animal {
      name: string;
    
      constructor(name: string) {
        super();
        this.name = name;
      }
    }
    
    let dog: Dog = new Dog('Bob');
    
    dog.eat();      // eat something... ...
    抽象类中的抽象方法,不包含具体实现并且必须在子类中实现
    abstract class Animal {
      abstract sleep(): void;
    }
    
    class Dog extends Animal {
      name: string;
    
      constructor(name: string) {
        super();
        this.name = name;
      }
    
      sleep() {
        console.log('dog sleep... ...')
      }
    }
    
    let dog: Dog = new Dog('Bob');
    dog.sleep();    // dog sleep... ...
    abstract class Animal {
      abstract sleep(): void;
    }
    
    class Dog extends Animal {
      name: string;
    
      constructor(name: string) {
        super();
        this.name = name;
      }
    }
    
    let dog: Dog = new Dog('Bob');
    // Non-abstract class 'Dog' does not implement inherited abstract member 'sleep' from class 'Animal'.
    抽象类也可以实现 多态,多态就是在父类中定义一个抽象方法,在多个子类中,可以对这个方法有不同的实现,程序运行时会根据不同的对象执行不同的操作,这样就实现了运行时的绑定
    abstract class Animal {
      abstract sleep(): void;
    }
    
    class Dog extends Animal {
      sleep() {
        console.log('dog sleep');
      }
    }
    
    class Cat extends Animal {
      sleep() {
        console.log('cat sleep');
      }
    }
    
    let dog: Dog = new Dog();
    let cat: Cat = new Cat();
    
    let animals: Animal[] = [dog, cat];
    
    animals.forEach(item => item.sleep());
    
    // dog sleep
    // cat sleep
    6、类与接口

    6-1、类与接口之间的关系
     
     (1)、类实现接口的时候,必须实现接口中声明的所有属性
     (2)、接口只能约束类的公用成员
     (3)、接口不能约束类的构造函数
    // 示例一:类实现接口的时候,必须实现接口中声明的所有属性
    
    interface Animal {
      name: string;
      eat(): void;
    }
    
    class Dog implements Animal {
      name: string;
      
      constructor(name: string) {
        this.name = name;
      }
    }
    
    let dog = new Dog('Bob');
    // Class 'Dog' incorrectly implements interface 'Animal'.
    // Property 'eat' is missing in type 'Dog' but required in type 'Animal'.
    
    
    // 示例二:接口只能约束类的公用成员
    
    interface Animal {
      name: string;
      eat(): void;
    }
    
    class Dog implements Animal {
      private name: string;   // 约束为私有属性
      
      constructor(name: string) {
        this.name = name;
      }
    
      eat() {
        console.log('dog like bones');
      }
    }
    
    let dog = new Dog('Bob');
    
    // Class 'Dog' incorrectly implements interface 'Animal'.
    // Property 'name' is private in type 'Dog' but not in type 'Animal'.
    
    
    // 示例三:接口不能约束类的构造函数
    
    interface Animal {
      name: string;
      new(name: string): void;   // 在接口中约束构造函数
      eat(): void;
    }
    
    class Dog implements Animal {
      name: string;
      
      constructor(name: string) {
        this.name = name;
      }
    
      eat() {
        console.log('dog like bones');
      }
    }
    
    let dog = new Dog('Bob');
    
    // Class 'Dog' incorrectly implements interface 'Animal'.
    // Type 'Dog' provides no match for the signature 'new (name: string): void'.
    6-2、接口的继承
     
    接口可以像类一样相互继承,并且一个接口可以继承多个接口

    接口的继承的好处是:可以抽离出可重用的接口,也可以将多个接口合并成一个接口
    interface Human {
      name: string;
    }
    
    interface Man extends Human {
      run(): void;
    }
    
    interface Child {
      cry(): void;
    }
    
    interface Boy extends Man, Child {}
    
    // Error:必须实现所继承的所有父接口
    let boy: Boy = {};
    // Type '{}' is missing the following properties from type 'Boy': run, name, cry
    
    
    let boy: Boy = {
      name: 'Jorge',
      run() {},
      cry() {}
    };
    6-3、接口继承类
     
    相当于接口把类的成员都抽象了出来,只有类的成员结构,而没有具体的实现、
    class Point {
      x: number;
      y: number;
    }
    
    interface Point3d extends Point {
      z: number;
    }
    
    let point3d: Point3d = {x: 1, y: 2, z: 3};
    接口在抽离类的成员时,不仅抽离了公共成员,也会抽离私有成员和受保护成员
    class Point {
      private x: number;    // 私有成员
      public y: number;
    }
    
    interface Point3d extends Point {
      z: number;
    }
    
    // Error:属性x是私有成员,是不能被子类或实例调用的。
    // 这个报错也恰好说明了,接口抽离类时,抽离了私有成员,导致了下面的报错
    let point3d: Point3d = {x: 1, y: 2, z: 3};
    
    // 不能将类型“{ x: number; y: number; z: number; }”分配给类型“Point3d”。
    // 属性“x”在类型“Point3d”中是私有属性,但在类型“{ x: number; y: number; z: number; }”中不是
    接口与类之间的关系:

    (1)、接口之间是可以相互继承的,这样可以实现接口之间的复用

    (2)、类之间也是可以相互继承的,可以实现类中属性和方法的复用

    (3)、类可以实现接口,但是接口只能约束类的公用成员

    (4)、接口可以继承类,相当于接口抽离了类的所有成员包括 公用、私有和受保护成员
  • 相关阅读:
    Beats数据采集---PacketbeatFilebeatTopbeatWinlogBeat使用指南
    《书读完了》—— 随笔
    《历史的天空》—— 读后总结
    Hadoop HDFS 用户指南
    单节点部署Hadoop教程
    [收藏]IntelliJ Idea快捷键
    《鬼谷子的局5》—— 读后总结
    Logstash为什么那么慢?—— json序列化
    《一线架构师实践指南》—— 读后总结
    Oracle Redo 以及 Archived日志简述
  • 原文地址:https://www.cnblogs.com/rogerwu/p/12193236.html
Copyright © 2011-2022 走看看