介绍:从ECMAScript 2015,也就是ECMAScript 6开始,JavaScript程序员将能够使用基于类的面向对象的方式,在ts中也可以使用,举个例子

  1. class name {
  2. name:string
  3. constructor(name: string) {
  4. this.name = name
  5. }
  6. setName() {
  7. }
  8. }

声明一个name类,含有setName方法。

继承

在Ts中继承和在js中的继承是一样的都是使用extends关键字

  1. class name {
  2. name:string
  3. constructor(name: string) {
  4. this.name = name
  5. }
  6. setName() {
  7. }
  8. }
  9. class dog extends name {
  10. constructor(name:string) {
  11. super(name);
  12. }
  13. }

这是类最基本的继承:类冲基类中继承了属性和方法,dog是一个爬升类,来自name的基类,派生出来的类叫做子类,基类称之为超类,派生类必须调用super();在子类中可以重写同名的方法

公共、私有,受保护的修饰符

在TS中 默认为public(公共的)。

上面的例子我们可以改造一下

  1. class name {
  2. public name:string
  3. constructor(name: string) {
  4. this.name = name
  5. }
  6. setName() {
  7. }
  8. }
  9. class dog extends name {
  10. constructor(name:string) {
  11. super(name);
  12. }
  13. }

private(私有)

当属性被标记成private时,不能在声明它的类的外部访问
比如:

  1. class name {
  2. private name:string
  3. constructor(name: string) {
  4. this.name = name
  5. }
  6. setName() {
  7. }
  8. }
  9. let newname = new names("").name //error 属性“name”为私有属性,只能在类“name”中访问

另外,当我们比较两种类型时,并不在乎他从何而来,如果所有的成员类型都是兼容的,那我们就认为他类型兼容。
举以下官网的例子:

  1. class Animal {
  2. private name: string;
  3. constructor(theName: string) { this.name = theName; }
  4. }
  5. class Rhino extends Animal {
  6. constructor() { super("Rhino"); }
  7. }
  8. class Employee {
  9. private name: string;
  10. constructor(theName: string) { this.name = theName; }
  11. }
  12. let animal = new Animal("Goat");
  13. let rhino = new Rhino();
  14. let employee = new Employee("Bob");
  15. animal = rhino;
  16. animal = employee; // 错误: Animal 与 Employee 不兼容.

解释:Animal和Rhino 两个类,Rhino 是Animal的子类,Employee看似和Animal相同,但互相赋值之后会发生错误,因为Animal和Rhino 共享来自Animal的私有成员private name: string;,所以他们兼容。但是Employee 却不相同,因为两者不是来自同一个类

protected(受保护的,只能在父类及其子类中(派生类)访问)

  1. class Person {
  2. protected name: string
  3. constructor(name: string) {
  4. this.name = name
  5. }
  6. }
  7. class Emp extends Person {
  8. private de: string
  9. constructor(name: string, de: string) {
  10. super(name)
  11. this.de = de
  12. }
  13. public set(){
  14. this.name
  15. }
  16. public get(){
  17. return "helo"
  18. }
  19. }
  20. let a = new Emp("a","");
  21. a.get
  22. a.name //error name 为protected 所以不能再外部访问,但是在子类Emp中可以访问,因为Emp由Person派生出来的
  23. a.de //error de 为私有 只能在Emp中访问也就就是父类中才能被访问ss

构造函数也能被标记 protected,这意味这个类不能在包含他的类外部实现,可以被继承(也就是父类不能实例化,但是被继承的子类是可以的实例化的。)

  1. class Person {
  2. protected name: string
  3. protected constructor(name: string) {
  4. this.name = name
  5. }
  6. }
  7. let NewPerson = new Person(); //error 类“Person”的构造函数是受保护的,仅可在类声明中访问
  8. class Emp extends Person {
  9. constructor(public name:string){
  10. super(name)
  11. }
  12. }
  13. let NewEmp = new Emp('') //正确~

readonly 只读

设置属性为只读,只能在声明或者构造函数里初始化。

  1. class Person {
  2. readonly name: string
  3. constructor(name: string) {
  4. this.name = name
  5. }
  6. }
  7. let NewEmp = new Person('')
  8. NewEmp.namev = '' //error 只能读取不能赋值
  9. interface pers {
  10. readonly name :string
  11. }
  12. let sum:pers = {
  13. name:'aaa'
  14. }
  15. sum.name //正确
  16. sum.name = 66; //error name为只读属性

参数属性

在上面的例子中,Person有一个只读成员name,并且作为构造函数,还可能立刻赋值,参数属性可以帮我们简化操作

  1. class Person {
  2. readonly name: string
  3. constructor( name: string) {
  4. this.name = name
  5. }
  6. }
  7. --------------使用参数属性后------------------
  8. class Person {
  9. constructor(readonly name: string ="") {
  10. this.name = name
  11. }
  12. } //简化了步骤

存取器(提供了get和set方法)

  1. class Person {
  2. constructor(private name: string = "") {
  3. this.name = name
  4. }
  5. get getName(): string {
  6. return this.name;
  7. }
  8. set getName(name: string) {
  9. this.name = name
  10. }
  11. }

注意事项: 存取器要求将编译器设置ECMAScript 5或更高,不能降低到ECMAScript 3,另外只有get不带set的存取器,系统默认推断为 readonly。

静态属性

上面我们操作的都是类的实例成员,也可以操作类的静态成员,这属性存在类的本身而不是存在类的实例上,使用方法是在属性或者方法前增加 static ,这样每个实例想要访问这个属性的时候。只需要增加类名即可。

  1. class Person {
  2. constructor(private name: string = "") {
  3. this.name = name
  4. }
  5. static getname() {
  6. }
  7. }
  8. Person.getname() //不需要示例化就可以访问

抽象类

抽象类作为其他派生类的基类使用,一般不会直接被实例化,不同于接口,抽象类可以包含成员的细节实现,abstract关键字用于定义抽象类和在抽象类内部定义的抽象方法。

  1. abstract class Person {
  2. abstract name(): void //标记为 abstract的属性或者方法不能实现
  3. setname(): void {
  4. console.log("name is xxx")
  5. }
  6. }

抽象类中的抽象方法不包含具体实现并且必须在派生类中实现,抽象方法的语法与接口方法相似,两者都定义方法签名但不包含方法体,

  1. abstract class Department {
  2. constructor(public name: string) {
  3. }
  4. printName(): void {
  5. console.log('Department name: ' + this.name);
  6. }
  7. abstract printMeeting(): void; // 必须在派生类中实现
  8. }
  9. class AccountingDepartment extends Department {
  10. constructor() {
  11. super('Accounting and Auditing'); // 在派生类的构造函数中必须调用 super()
  12. }
  13. printMeeting(): void {
  14. console.log('The Accounting Department meets each Monday at 10am.');
  15. }
  16. generateReports(): void {
  17. console.log('Generating accounting reports...');
  18. }
  19. }
  20. let department: Department; // 允许创建一个对抽象类型的引用
  21. department = new Department(); // 错误: 不能创建一个抽象类的实例
  22. department = new AccountingDepartment(); // 允许对一个抽象子类进行实例化和赋值
  23. department.printName();
  24. department.printMeeting();
  25. department.generateReports(); // 错误: 方法在声明的抽象类中不存在,因为department标记的是抽象方法Department,而抽象方法不包含generateReports方法,generateReports方法在派生类中。

构造函数

在ts中声明一个类的实行,实际上声明了类的实例类型

  1. class Greeter {
  2. greeting: string;
  3. constructor(message: string) {
  4. this.greeting = message;
  5. }
  6. greet() {
  7. return "Hello, " + this.greeting;
  8. }
  9. }
  10. let greeter: Greeter; //表示greeter 是一个构造函数
  11. greeter = new Greeter("world"); //此处必须使用new 否则报错
  12. console.log(greeter.greet());
  13. let greeterMaker: typeof Greeter = Greeter; // 此处表示greeterMaker 的类型必须是Greeter
  14. //typeof Greeter 获取Greeter的类型
  15. greeterMaker.standardGreeting = "Hey there!"; //静态属性可以直接获取
  16. let greeter2: Greeter = new greeterMaker();
  17. console.log(greeter2.greet());

把类当作接口来使用

  1. class Point {
  2. x: number = 1; //必须赋值否则报错
  3. y: number = 2;
  4. }
  5. interface Point3d extends Point { //接口继承了类
  6. z: number;
  7. }
  8. let point3d: Point3d = { x: 1, y: 2, z: 3 }; //该属性必须包含三个值

知识总结:

  1. 1.typescript中使用类时基本和js一致,可以使用继承(extends),子类继承了父类的方法,也可以重写方法。
  2. 2.typescript中使用类时,可以对属性限定(pubilc【共有】,private【私有】,protected【受保护】)三种,默认为public
  3. 3.pubilc表示能够被实例外部访问
  4. 4.private 表示不能在它声明的类的外部访问,派生类也不能访问。private存在兼容性,子类和父类同时被实例化时,父类和子类可以互相赋值,因为他们共享了父类。
  5. 5.protected 受保护的,派生类中可以访问。
  6. 6.函数被标记成protected时,这个类就不能被包含它的类外部被实例化,但是能被继承。
  7. 7.readonly修饰符,被readonly修饰的属性,只能读取,不能赋值。并且只读属性必须在声明时或者构造函数里初始化
  8. 8.参数属性可以简化代码
  9. class Octopus {
  10. namestring //舍弃
  11. constructor(readonly name: string) {
  12. }
  13. }
  14. 9.存取器(get,set),Emscript5以上才可以,如果只有get,会被定义为只读rreadonly
  15. 10.抽象类作为派生类的基类使用,一般不会被实例化,不同于接口,抽象类可以包含成员实现的细节。
  16. abstract关键字时用于定义抽象类和在抽象类内部定义的抽象方法,抽象类中的抽象方法不包含具体实现且必须在派生类中实现。
  17. 11.构造函数可以当成类的实例类型使用。 typeof 可以拿到类的类型
  18. 12.类还可以当作接口使用。