跳到主要内容位置

TypeScript:面向对象特性之多态

多态,是说一种物质,有多种状态,在很多生活场景中都能遇到这种现象。当组装电脑的时候,我们知道主板上有 CPU、显卡接口,对于显卡来说,只要接口型号匹配,那么不管它的厂家是谁、性能如何,它都能正常的发挥作用。在编程世界里,多态表现为,在创建对象的时候,不需要知道它具体是由哪个 class 创建的,但是通过继承或接口,我明确知道它包含一些方法和属性,这样我能安全的调用它们。接下来,分别看一下,在 typescript 中使用继承方式和接口方式实现多态。

继承方式

假设我们有一个普通按钮组件和链接按钮组件,普通按钮组件渲染为 Button 标签,链接按钮渲染为 a 标签,并且链接按钮继承自普通按钮组件,以复用 name 属性和其他代码,这里我们只关注一下 render,渲染方法:

// Button
class Button {
protected name: string;
constructor(name: string) {
this.name = name;
}
public render() {
console.log(`<Button>${this.name}</Button>`);
}
// 其它代码
}

// LinkButton
class LinkButton extends Button {
public render() {
console.log(`<a>${this.name}</a>`);
}
}

这里,如果我有一组按钮组件需要渲染,包括普通按钮和链接按钮,那么可以这样做:

  • 定义一个数组,通过尖括号泛型,规定里边存放 Button 组件
  • 但是它里边的值既可以是 Button 的对象,也可以是 LinkButton 的对象
  • 这里有一个转型机制,所有 Button 的子类对象都会自动转换为 Button 父类对象
  • 后边渲染的时候,因为知道子类会继承父类的方法,所以可以直接调用 render 方法来渲染组件:
let comps: Array<Button> = [new Button("普通按钮"), new LinkButton("链接按钮")];

// <Button>普通按钮</Button>
// <a>链接按钮</a>
comps.forEach((comp) => comp.render());

这种方式有一个缺陷,如果有一个没有继承关系的组件,Image 组件,也要放到数组里一起渲染的话,就会出错:

class Image {
public render() {
console.log(`<img />`);
}
// other methods
}

let comps: Array<Button> = [
new Button("普通按钮"),
new LinkButton("链接按钮"),
// Error: Property 'name' is missing in type 'Image' but required in type 'Button'.
new Image(),
];

继承方式适用于有继承关系的类,需要大量复用代码的情况下。

接口方式

上边的情况,我们可以使用接口的形式,这个就跟电脑主板一样了。我可以使用接口 interface,定义一个 Renderable 接口,可渲染的,在里边定义一个 render 方法:

  • 接口里的方法规范不需要方法体。这里规定,实现这个接口的类,必须要实现它里边规定的方法,那么所有实现这个接口的类,就能够保证一定有 render 方法。
interface Renderable {
render(): void;
}

接着,让 Button 和 LinkButton 实现这个接口,因为它们里边已经有 render 方法了,所以实现了 Renderable 接口,接下来定义一个 Image class,让它也实现 Renderable 接口,在 render 方法里打印出标签:

class Image implements Renderable {
public render() {
console.log(`<img />`);
}
// other methods
}

然后,我们可以把数组的泛型改为 Renderable,这样它里边的元素都会默认转换成 Renderable 这个接口类型:

  • 接着再调用它们里边的 render 方法,就都能够正常渲染了。
let comps: Array<Renderable> = [
new Button("普通按钮"),
new LinkButton("链接按钮"),
new Image(),
];

comps.forEach((comp) => comp.render());

接口形式适合没有继承关系的类且需要统一调用某个方法的情况。 好了,这个视频介绍了一下多态的基本概念,并通过继承和接口的方式实现了多态。如果觉得视频有帮助,请点赞并关注,我是峰华,感谢观看!

提示

一系列的课程让你成为高级前端工程师。课程覆盖工作中所有常用的知识点和背后的使用逻辑,示例全部都为工作项目简化而来,学完即可直接上手开发!

即使你已经是高级前端工程师,在课程里也可能会发现新的知识点和技巧,让你的工作更加轻松!

《React 完全指南》课程,包含 React、React Router 和 Redux 详细介绍,所有示例改编自真实工作代码。点击查看详情。

《Vue 3.x 全家桶完全指南与实战》课程,包括 Vue 3.x、TypeScript、Vue Router 4.x、Vuex 4.x 所有初级到高级的语法特性详解,让你完全胜任 Vue 前端开发的工作。点击查看详情。

《React即时通信UI实战》课程,利用 Storybook、Styled-components、React-Spring 打造属于自己的组件库。

《JavaScript 基础语法详解》本人所著图书,包含 JavaScript 全面的语法知识和新特性, 可在京东、当当、淘宝等各大电商购买