OveUI博客
老前端的戎码生涯

typescript学习之路(四) —— ts类的继承(包含es5以及es6的类继承)

上一文已经写了es5,es6等类的定义,所以本章主要写es5和es6的继承,由于es6的继承和ts的继承如出一辙,只是加了类型定义而已,所以ts的继承稍微写下,不会太详细。

es5继承

原型链继承

所谓原型链继承,就是将一个实例对象挂载到另一个原型上。即继承对象的prototype = new 实例化的对象

// 声明一个Parent构造函数
function Parent (name) {
this.name = name;

this.prientName = function () {
console.log(`我的名字叫${this.name}`)
}
}
Parent.prototype.print = function () {
console.lgo('hello,小胖纸')
}
// 在声明一个Child构造函数
function Child (age) {
this.age = age;

this.prientAge = function () {
console.log(this.age)
}
}
// 此时两个构造函数还是没有任何交集的,我们通过原型链的特性来进行继承
// 原型链特性,在prototype找不到会顺着__proto__一直向父级找,直到Function为止
Child.prototype = new Parent();
new Child('小胖纸').prientName() // 我的名字叫undefined
new Child('小胖纸').print () // hello,小胖纸

这样我们就实现了Child继承Parent的属性和方法,但是调用父类的方法我们发现,我们无法传值到父类里面,也就是父类的构造函数接收不到我们的传值,这也是原型链继承的一个弊端。

构造函数继承

想实现构造函数继承我们不得不依赖call或者apply,通过改变this指向

// 声明一个Parent构造函数
function Parent (name) {
this.name = name;

this.prientName = function () {
console.log(`我的名字叫${this.name}`)
}
}
Parent.prototype.print = function () {
console.lgo('hello,小胖纸')
}
// 在声明一个Child构造函数
function Child (name, age) {
// 借用call方法,我们更改Parent内部this的指向,使其指向Child的实例化化对象,同时也可将值进行传递
Parent.call(this, name)
this.age = age;

this.prientAge = function () {
console.log(this.age)
}
}
new Child('小胖纸').prientName() // 我的名字叫小胖纸
new Child().print () // error print is not a function

这样我们就能实现继承并且解决不能传值的问题,但是你也发现了,我们这样更改是这样继承不了来自原型的公共属性和方法,而只能在构造函数内部去一一创建,性能不佳。

组合继承

顾名思义,组合继承就是既有原型链继承又有构造函数继承,这样既能解决不能传值的问题,也解决了不能共有的属性和方法

// 声明一个Parent构造函数
function Parent (name) {
this.name = name;

this.prientName = function () {
console.log(`我的名字叫${this.name}`)
}
}
Parent.prototype.print = function () {
console.lgo('hello,小胖纸')
}
// 在声明一个Child构造函数
function Child (name, age) {
// 借用call方法,我们更改Parent内部this的指向,使其指向Child的实例化化对象,同时也可将值进行传递
Parent.call(this, name)
this.age = age;

this.prientAge = function () {
console.log(this.age)
}
}
Child.prototype = new Parent();

new Child('小胖纸').prientName() // 我的名字叫小胖纸
new Child().print () // hello,小胖纸

但是这样也有一个小问题,那就是构造函数已经实现了继承父类的属性和方法,只是没法继承公有属性和方法而已,而原型链继承呢?可以继承公有属性和方法,只是不能传值而已,它一样也能继承父类内部的属性和方法,是不是有一小部分重叠?所以嘞,我们稍微改造下Child.prototype = Parent.prototype把子类中的原型直接指向父类的原型就可以啦,这样,构造函数专门来继承父类内部的属性和方法以及传值,而原型链则管着公有属性和方法,各司其职。

es6继承

es6的继承主要是extends关键字,这比es5 的实现继承,要清晰和方便很多。

// 定义一个父类
class Parent {
constructor (name) {
this.name = name;
}

prientName () {
console.log(this.name)
}
}
// 定义子类继承父类
class Child extends Parent {
constructor (name, age) {
super(name);
this.age = age;
}
}

这样我们就实现了继承,但是如何传值到父类中呢?这个时候我们还有另外一个关键字super,super什么意思呢?在子类的构造函数中使用就是调用父类的构造函数,当然也可以进行传参。在方法中使用super,可以直接调用父类的方法

// 定义一个父类
class Parent {
constructor (name) {
this.name = name;
}

prientName (age) {
console.log(`我是个${age}岁的${this.name}`)
}
}
// 定义子类继承父类
class Child extends Parent {
constructor (name, age) {
super(name);
this.age = age;
}

printAge () {
super.prientName (this.age)
}
}
let p1 = new Child('小胖纸', 22);
p1.printAge () // 我是个22岁的小胖纸

上面的代码中constructor 和printAge ,都出现了super关键字,它在这里表示父类的构造函数,用来新建父类的this对象。你可以把super理解成父类的实例化对象。当然这是在普通方法中,在类的静态方法中,super则变成了指代父类对象而不是类的实例的对象。

子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super方法,子类就得不到this对象。看列子

// 定义一个父类
class Parent {
constructor (name) {
this.name = name;
}
}
// 定义子类继承父类
class Child extends Parent {
constructor (age) {
this.age = age;
}
}
// 因为没有super调用父类中的constructor构造函数,所以实例化对象时报错
new Child(22) // error

// 定义一个父类
class Parent {
constructor (name) {
this.name = name;
}
}
// 定义子类继承父类
class Child extends Parent {
constructor (name, age) {
// this只能在super后面使用
this.age = age; // error
super(name)
}
}

但是他允许你这样

// 定义一个父类
class Parent {
constructor (name) {
this.name = name;
}
}
// 定义子类继承父类
class Child extends Parent {

}

当子类没有constructor时,会默认添加constructor,并调用super方法,但是你要是写上constructor时,那么就必须调用super。

当然如果父类中有static静态属性和方法,也一样会被继承到子类中

ts继承

只是加上了类型而已,如果不知道,请看上一篇文章,ts类的定义

// 定义一个父类
class Parent {
// 必须声明
name: string;
constructor (name: string) {
this.name = name;
}

prientName (): void {
console.log(this.name)
}
}
// 定义子类继承父类
class Child extends Parent {
// 必须声明
age: number;
// age? 表示存在不确定
constructor (name: string, age?: number) {
super(name);
this.age = age;
}
}
// 声明p 是Parent类型
let p: Parent = new Child('小胖纸');
// 声明p1 是Child类型
let p1: Child= new Child('大胖纸');
赞(0)
未经允许不得转载:UI树欲静 » typescript学习之路(四) —— ts类的继承(包含es5以及es6的类继承)