抽象类中不能有private的成员_6、typescript中的类

在es和 typescript 中,类成员的属性都是实例属性,而不是原型属性;类成员的方法都是原型方法。不同的是typescript 中类成员的属性必须有初始值,或者是在构造函数中被初始化

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 方法只在原型上,不在实例上

类的继承

与es一样,typescript 中类的继承也是通过 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.

成员修饰符

public

在 typescript 中,类的所有成员都默认为 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... ...

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

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.
 

成员修饰符不能用于接口中

interface Point {
   public y: number;
}
// 'public' modifier cannot appear on a type member.(1070)

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.

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) {
  }
  // name: string;   // 标识符“name”重复
}
//会被编译成下面的代码

"use strict";
class People {
    constructor(name) {
        this.name = name;
    }
}
 

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

抽象类

所谓抽象类,指的是只能被继承,不能被实例化的类。抽象类的好处是可以抽离出一些事物的共性,有利于代码的复用和扩展。使用 abstract 关键字定义抽象类和在抽象类内部定义的抽象方法

// 抽象类可以被继承
abstract class Animal {
  eat() {
    console.log('eat something... ...')
  }
}

class Dog extends Animal {
  name: string;

  constructor(name: string) {
    super();
    this.name = name;
  }
}
let a: Animal = new Animal();   // 无法创建抽象类的实例

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

类与接口

类实现接口的时候,必须实现接口中声明的所有属性

// 示例一:类实现接口的时候,必须实现接口中声明的所有属性

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'.

接口的继承

接口可以像类一样相互继承,并且一个接口可以继承多个接口

接口的继承的好处是:可以抽离出可重用的接口,也可以将多个接口合并成一个接口

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() {}
};

接口继承类

接口可以继承一个类,相当于接口把类的成员都抽象了出来,只有类的成员结构,而没有具体的实现。

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. 接口可以继承类,相当于接口抽象了类的所有成员包括 公用、私有和受保护成员

5b9bcfa3c64086b762447102f84139dd.png

摘抄自:Typescript 实战 --- (5)类

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值