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)、接口可以继承类,相当于接口抽离了类的所有成员包括 公用、私有和受保护成员
  • 相关阅读:
    Do You See Me? Ethical Considerations of the Homeless
    ELDER HOMELESSNESS WHY IS THIS AN ISSUE?
    Endoflife support is lacking for homeless people
    html内联框架
    html字体
    html块 div span
    html列表
    html表格
    SQL Server管理员专用连接的使用   作为一名DBA,经常会处理一些比较棘手的服务无响应问题,鉴于事态的严重性,多数DBA可能直接用“重启”大法,以便尽快的恢复生产环境的正常运转,但是多数情况
    如何配置最大工作线程数 (SQL Server Management Studio)
  • 原文地址:https://www.cnblogs.com/rogerwu/p/12193236.html
Copyright © 2011-2022 走看看