【Typescript】知识梳理:接口和类

575 阅读7分钟

类和接口的相关概念

在面向对象语言中,接口(Interfaces)是对行为的抽象,而具体如何行动需要由类(classes)去实现(implement)。

  • 类(Class):定义了一件事物的抽象特点,包含它的属性和方法
  • 对象(Object):类的实例,通过 new 生成
  • 面向对象(OOP)的三大特性:封装、继承、多态
  • 封装(Encapsulation):将对数据的操作细节隐藏起来,只暴露对外的接口。外界调用端不需要(也不可能)知道细节,就能通过对外提供的接口来访问该对象,同时也保证了外界无法任意更改对象内部的数据
  • 继承(Inheritance):子类继承父类,子类除了拥有父类的所有特性外,还有一些更具体的特性
  • 多态(Polymorphism):由继承而产生了相关的不同的类,对同一个方法可以有不同的响应。比如 CatDog 都继承自 Animal,但是分别实现了自己的 eat 方法。此时针对某一个实例,我们无需了解它是 Cat 还是 Dog,就可以直接调用 eat 方法,程序会自动判断出来应该如何执行 eat
  • 存取器(getter & setter):用以改变属性的读取和赋值行为
  • 修饰符(Modifiers):修饰符是一些关键字,用于限定成员或类型的性质。比如 public 表示公有属性或方法
  • 抽象类(Abstract Class):抽象类是供其他类继承的基类,抽象类不允许被实例化。抽象类中的抽象方法必须在子类中被实现
  • 接口(Interfaces):不同类之间公有的属性或方法,可以抽象成一个接口。接口可以被类实现(implements)。一个类只能继承自另一个类,但是可以实现多个接口

接口interface

接口interface是一系列抽象方法的声明,是一些方法特征的集合,这些方法都应该是抽象的,需要由具体的类去实现,然后第三方就可以通过这组抽象方法调用,让具体的类执行具体的方法。

interface Test {
    name: string;
    age?: number;//?:可选属性
    readonly id: number;//readonly只读属性,不可赋值
    //注意,只读的约束存在于第一次给对象赋值的时候,而不是第一次给只读属性赋值的时候
    [index:number]:string //接口中可以将数组的索引值和元素设置为不同类型,索引值可以是数字或字符串。
}

联合类型和接口

interface RunOptions { 
    program:string; 
    commandline:string[]|string|(()=>string); 
} 
 
// commandline 是字符串
var options:RunOptions = {program:"test1",commandline:"Hello"}; 
console.log(options.commandline)  
 
// commandline 是字符串数组
options = {program:"test1",commandline:["Hello","World"]}; 
console.log(options.commandline[0]); 
console.log(options.commandline[1]);  
 
// commandline 是一个函数表达式
options = {program:"test1",commandline:()=>{return "**Hello World**";}}; 
 
var fn:any = options.commandline; 
console.log(fn());

接口和数组

接口中可以将数组的索引值和元素设置为不同类型,索引值可以是数字或字符串。

需要注意的是,一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集

interface namelist { 
   [index:number]:string 
} 
 
var list2:namelist = ["John",1,"Bran"] // 错误元素 1 不是 string 类型
interface ages { 
   [index:string]:number 
} 
 
var agelist:ages; 
agelist["John"] = 15   // 正确 
agelist[2] = "nine"   // 错误

接口继承

接口继承指接口可以通过其他接口来扩展自己。Typescript 允许接口继承多个接口。继承使用关键字 extends

单接口继承语法格式:

Child_interface_name extends super_interface_name

多接口继承语法格式:

Child_interface_name extends super_interface1_name, super_interface2_name,…,super_interfaceN_name

继承的各个接口使用逗号 , 分隔。

类class

属性和方法

定义类的关键字为 class,后面紧跟类名,类可以包含以下几个模块(类的数据成员):

  • 字段 − 字段是类里面声明的变量。字段表示对象的有关数据。
  • 构造函数 − 类实例化时调用,可以为类的对象分配内存。
  • 方法 − 方法为对象要执行的操作。

使用 class 定义类,使用 constructor 定义构造函数。

通过 new 生成新实例的时候,会自动调用构造函数。

class Car { 
   // 字段
   engine:string;    
   // 构造函数
   constructor(engine:string) { 
   //this 关键字表示当前类实例化的对象。构造函数的参数名与字段名相同。
      this.engine = engine 
   }    
   // 方法
   disp():void { 
      console.log("函数中显示发动机型号  :   "+this.engine) 
   } 
} 
//使用 new 关键字来实例化类的对象,类实例化时会调用构造函数,类中的字段属性和方法可以使用 **.** 号来访问。
var obj = new Car("XXSY1")
// 访问字段
console.log("读取发动机型号 :  "+obj.engine)  
// 访问方法
obj.disp()

访问控制修饰符(关键字)

TypeScript 中,可以使用访问控制符来保护对类、变量、方法和构造方法的访问。TypeScript 支持 3 种不同的访问权限。

  • public(默认) : 公有,可以在任何地方被访问。
  • protected : 受保护,可以被其自身以及其子类和父类访问。当构造函数修饰为 protected 时,该类只允许被继承。
  • private : 私有,只能被其定义所在的类访问。使用 private 修饰的属性或方法,在子类中也是不允许访问的。当构造函数修饰为 private 时,该类不允许被继承或者实例化。

readonly

  • 只读属性关键字,只允许出现在属性声明或索引签名或构造函数中。
  • 如果 readonly 和其他访问修饰符同时存在的话,需要写在其后面。

abstract

  • 用于定义抽象类和其中的抽象方法。
  • 抽象类是不允许被实例化的。
  • 抽象类中的抽象方法必须被子类实现。

存取器

使用 getter 和 setter 可以改变属性的赋值和读取行为。

class Animal {
  constructor(name) {
    this.name = name;
  }
  get name() {
    return 'Jack';
  }
  set name(value) {
    console.log('setter: ' + value);
  }
}
​
let a = new Animal('Kitty'); // setter: Kitty
a.name = 'Tom'; // setter: Tom
console.log(a.name); // Jack

静态方法

使用 static 修饰符修饰的方法称为静态方法,它们不需要实例化,而是直接通过类来调用。

class StaticMem {  
   static num:number; 
   
   static disp():void { 
      console.log("num 值为 "+ StaticMem.num) 
   } 
} 
 
StaticMem.num = 12     // 初始化静态变量
StaticMem.disp()       // 调用静态方法

instanceof 运算符

instanceof 运算符用于判断对象是否是指定的类型,如果是返回 true,否则返回 false。

class Person{ } 
var obj = new Person() 
var isPerson = obj instanceof Person; 
console.log("obj 对象是 Person 类实例化来的吗? " + isPerson);

类的继承

类继承使用关键字 extends,子类除了不能继承父类的私有成员(方法和属性)和构造函数,其他的都可以继承。

TypeScript 一次只能继承一个类,不支持继承多个类,但 TypeScript 支持多重继承(A 继承 B,B 继承 C)。

class child_class_name extends parent_class_name

interface与class的区别和使用

  • interface:接口只声明成员方法,不做实现(抽象)。
  • class:类声明并实现方法。

类实现接口

interface ContentInterface {
    getContent(): String;
    //每个实现该接口的类都必须实现getContent方法
}
​
class Article implements ContentInterface {
    // 必须实现getContent方法
    public function getContent(): String {
        return 'I am an article.';
    } 
}
​
class Passage implements ContentInterface {
    // 但实现方式可以不同
    public function getContent(): String {
        return 'I am a passage.'
    }
}
​
class News implements ContentInterface {
    // 没有实现getContent方法,编译器会报错
}
​
let a = new Article();
let p = new Passage();
​
print(a); // "I am an article."
print(p); // "I am a passage."

接口继承类

类定义会创建两个东西:类的实例类型和一个构造函数。 因为类可以创建出类型,所以你能够在允许使用接口的地方使用类。

class Point {
    x: number;
    y: number;
}
​
interface Point3d extends Point {
    z: number;
}
​
let point3d: Point3d = {x: 1, y: 2, z: 3};

extends和implements区别和使用

  • 在类的声明中,通过关键字extends来创建一个类的子类。关键字implements声明类使用一个或者多个接口。
  • extends 可为类继承某个类, 获取父类的所有的静态属性,继承之后可以使用父类的方法, 也可以重写父类的方法。
  • extends 可做接口的接口(类)继承,通过其他接口(类)来扩展自己,可多接口继承。
  • implements 可实现类的多个接口继承, 接口的方法一般为空的, 必须重写方法才能使用 。
class A(类) extends B(类) implements C,D,E(接口)//类
    
Child_interface(接口) extends super_interface1(接口), super_interface2(接口),…,super_interfaceN(接口)//接口