入个TypeScript的门(6)——类

118 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第12天,点击查看活动详情

ES6新增了class关键字,类的概念。而类主要是实现了面向对象开发(其实我看好多文章说页面开发最好不要用class...),那么它就有属于面向对象开发的特点,而TS中的类的用法就又比JS中的更专业一点了,比如继承、封装、多态以及抽象、私有、公共等。

public、private 和 protected

在属性上

  • public指的是公共的,可以被子类、自身或者实例访问到的;
  • protected指的是受保护的,仅仅可以被子类或者自身访问到;
  • private指的是私有的,也就是只能自己访问到;
class Person {
  private features: string = "直立行走";
  protected sex;
  constructor(public name: string, public age: number, sex: string) {
    this.name = name;
    this.age = age;
    this.sex = sex;
  }
}

let p = new Person('xiaolei',18,'男');
console.log(p.name,p.age);
console.log(p.sex);  //报错:属性“sex”受保护,只能在类“Person”及其子类中访问。
console.log(p.features);  //报错:属性“features”为私有属性,只能在类“Person”中访问。

在子类中:

class Person {
  private features: string = "直立行走";
  protected sex;
  constructor(public name: string, public age: number, sex: string) {
    this.name = name;
    this.age = age;
    this.sex = sex;
    console.log(this.features); //直立行走
  }
}

class Man extends Person {
  constructor(...args:any[]) {
    super(...args as [string,number,string]);
    console.log(this.sex);
    console.log(this.name);
    console.log(this.age);
    console.log(this.features); //报错:属性“features”为私有属性,只能在类“Person”中访问。
  }
}

let l = new Man('xiaolei',18,'男人');

在static上

但是我们要清楚这个private只能在自己访问到的意思是能在Class内部访问到,它还是添加在Person.prototype上面的,而不是直接加在Person上面,我们直接使用Person.reatures是获取不到的。想要通过Person.features获取到得使用static关键字(但是不能使用private关键字,不然还是)。

class Person {
  static features: string = "直立行走";
  constructor(public name: string) {
    this.name = name;
    console.log(this.features); //直立行走
  }
}
console.log(Person.features); //报错:类型“typeof Person”上不存在属性“features”。

但是如果使用了private关键字修饰的话,那还是只能在Class内部使用:

class Person {
  private static features: string = "直立行走";
  constructor(public name: string) {
    this.name = name;
    console.log(Person.features); //直立行走
  }
}

去掉private关键字(也就是使用public关键字)就可以在外部通过Person获取到了:

class Person {
  static features: string = "直立行走";
  constructor(public name: string) {
    this.name = name;
  }
}
console.log(Person.features); 

在构造函数上

此外,private、protect和public还可以用在方法或者构造函数上(默认加的是public,就比如上面的例子默认函数其实加的就是public):

class Person {
  private features: string = "直立行走";
  protected sex;
  protected constructor(public name: string, public age: number, sex: string) {
    this.name = name;
    this.age = age;
    this.sex = sex;
  }
}

class Man extends Person {
  constructor(...args:any[]) {
    super(...args as [string,number,string]);
    console.log(this.sex);
  }
}

let l = new Man('xiaolei',18,'男人');
let x = new Person('xiaoxu',20,'女人');//报错:类“Person”的构造函数是受保护的,仅可在类声明中访问。

也就是说这个类只能被继承,却不能被实例化了。

如果是private,那么这个class就寄了,既不能被继承又不能被实例化了。

class Person {
  private features: string = "直立行走";
  protected sex;
  private constructor(public name: string, public age: number, sex: string) {
    this.name = name;
    this.age = age;
    this.sex = sex;
  }
}

class Man extends Person {  //报错:无法扩展类“Person”。类构造函数标记为私有。
  constructor(...args:any[]) {
    super(...args as [string,number,string]);
    console.log(this.sex);
  }
}

let l = new Man('xiaolei',18,'男人');
let x = new Person('xiaoxu',20,'女人');//报错:类“Person”的构造函数是受保护的,仅可在类声明中访问。

在方法上

当然public就不说了,那是在哪里都可以访问,就相当于any一样,适用于任何情况!

class Person {
  static features: string = "直立行走";
  protected sex;
  constructor(public name: string, public age: number, sex: string) {
    this.name = name;
    this.age = age;
    this.sex = sex;
    console.log(Person.features);
  }
  protected sayName() {
    console.log(this.name);
  }
  protected static sayFeatures() {
    console.log(Person.features);
  }
}
class Man extends Person {
  constructor(...args:any[]) {
    super(...args as [string,number,string]);
    Person.sayFeatures();
    this.sayName();
  }

}
let l = new Man("xiaolei", 18, "男人");
console.log(Person.sayFeatures()); //报错:属性“sayFeatures”受保护,只能在类“Person”及其子类中访问。

如果是private呢?

class Person {
  static features: string = "直立行走";
  protected sex;
  constructor(public name: string, public age: number, sex: string) {
    this.name = name;
    this.age = age;
    this.sex = sex;
    console.log(Person.sayFeatures());
    console.log(this.sayName());
  }
  private sayName() {
    console.log(this.name);
  }
  private static sayFeatures() {
    console.log(Person.features);
  }
}
class Man extends Person {
  constructor(...args:any[]) {
    super(...args as [string,number,string]);
    Person.sayFeatures(); //报错:属性“sayFeatures”为私有属性,只能在类“Person”中访问。
    this.sayName(); //报错:属性“sayName”为私有属性,只能在类“Person”中访问。
  }

}
let l = new Man("xiaolei", 18, "男人");
console.log(Person.sayFeatures()); //报错:属性“sayFeatures”为私有属性,只能在类“Person”中访问。

那么这个方法就只能自己调用了,在子类或者实例上都不能使用。

abstract

用于定义抽象类和其中的抽象方法。

抽象类

抽象类是不允许被实例化的:

abstract class Person {
  constructor(public name: string, public age: number) {
    this.name = name;
    this.age = age;
  }
}

let p = new Person("xiaolei", 18, "男人"); //报错:无法创建抽象类的实例。

那么抽象类就只能被继承咯,那么抽象类一般用来干嘛?一般抽象类就是用来声明抽象方法的。

抽象方法与多态

抽象方法只能在抽象类中被定义,抽象类中的方法只能声明不能实现,我们只能在继承抽象类的子类中去实现它且必须实现它。

abstract class Person {
  constructor(public name: string, public age: number) {
    this.name = name;
    this.age = age;
  }
  abstract sayName():void;
  abstract sayAge():void;
}

class Man extends Person { //报错:非抽象类“Man”不会实现继承自“Person”类的抽象成员“sayAge”。
  constructor(public name: string, public age: number) {
    super(name,age);
  }
  sayName() {
    console.log(this.name);
  };
}

我们实现这个方法就好了:

abstract class Person {
  constructor(public name: string, public age: number) {
    this.name = name;
    this.age = age;
  }
  abstract sayName(): void;
  abstract sayAge(): void;
}

class Man extends Person {
  constructor(public name: string, public age: number) {
    super(name, age);
  }
  sayName() {
    console.log(this.name);
  }
  sayAge(): void {
    console.log(this.age);
  }
}

同时我们不同的子类去实现相同的方法,但是内容却不一样,每个子类有不同的表现。

readonly

只读属性关键字,只允许出现在属性声明或索引签名或构造函数中。它的意思是这个值是只能get确不能set,就是只能读取,不能改变。

class Person {
  readonly name;
  constructor(name: string) {
    this.name = name;
  }
}
let l = new Person('xiaolei');
console.log(l.name);
l.name = 'xiaoxu'; //报错:无法分配到 "name" ,因为它是只读属性。

注意如果 readonly 和其他访问修饰符同时存在的话,需要写在其后面。

class Person {
  // public readonly name;
  public constructor(public readonly name) {
    // this.name = name;
  }
}

封装属性

下面我们就来说说类的封装(和JS的没啥区别)

class Person {
  // 不允许直接访问该属性,命名上一般以下划线_开头 
  private _sex:string = 'male';
  constructor(public name: string) {
    this.name = name;
  }
  public set sex(v : string) {
    if(v==='male' || v==='female') {
      this._sex = v;
    } else {
      console.log('性别只能是male或者female')
    }
  }
  public get sex() : string {
    return this._sex;
  }
}
let l = new Person('xiaolei', 'male');
console.log(l.sex);  // male
l.sex = '男';   //性别只能是male或者female