一、TS中类的继承的写法
TS中类通过关键字extends实现继承。
class Parent {}
class Son extends Parent {}
二、js实现类的继承
js实现继承有四种方式。
1.原型链继承
function Parent(name,age){
this.name=name
this.age=age
}
Parent.prototype.eat = function() {
console.log('eat')
}
function Child(favor,sex){
this.favor=favor
this.sex=sex
}
Child.prototype= new Parent("张三",23)
Child.prototype.constructor = Child
let child = new Child('篮球', '男')
child.eat()
原型链继承是通过将子类构造函数的prototype属性指向父类的实例实现继承。这样子类Child的实例可以访问父类Parent构造函数的原型对象上的属性和方法。
原型链继承的不足:不能通过子类构造函数向父类构造函数传递参数
2.借用构造函数继承
function Parent(name, age) {
this.name = name
this.age = age
}
Parent.prototype.eat = function () {
console.log('eat')
}
function Child(name, age, favor, sex) {
this.favor = favor
this.sex = sex
Parent.call(this, name, age)
}
let child = new Child("王二麻子", 34, "打篮球", "男");
console.log("child.eat", child.eat);//undefined
借用构造函数可以向父类构造函数传递参数,但是不足是:子类Child的实例无法访问父类Parent构造函数的原型对象上的属性和方法。
3.借用构造函数+原型链继承组合模式
function Parent(name, age) {
this.name = name
this.age = age
}
Parent.prototype.eat = function () {
console.log('eat')
}
function Child(name, age, favor, sex) {
this.favor = favor
this.sex = sex
Parent.call(this, name, age)//第二次调用构造函数
}
Child.prototype= new Parent("张三",23) //第一次调用构造函数
Child.prototype.constructor = Child
let child = new Child("王二麻子", 34, "打篮球", "男");
console.log("child.eat", child.eat);
该种组合模式,既有原型链继承的优点:子类可以调用父类构造函数的原型对象上的属性和方法。也具有借用构造函数继承的优点:可以向父类构造函数传递参数。但是不足之处是会调用两次父类的构造函数,且第一次调用父类构造函数传递的参数毫无意义。
4.寄生组合式继承
寄生组合式继承即寄生继承和借用构造函数继承的组合方式。其中寄生继承有多种实现方式。
方式一:借用辅助函数
function _extends(Parent, Child) {
function Middle() {
this.constructor = Child//将Child.prototype的constructor指向Child
}
Middle.prototype = Parent.prototype
Child.prototype = new Middle()
}
方式二:利用Object.create
function _extends(Parent, Child) {
Child.prototype = Object.create(Parent.prototype)
Child.prototype.constructor = Child
}
方式三:利用Object.setPrototypeOf
function _extends(Parent, Child) {
Object.setPrototypeOf(Child.prototype, Parent.prototype)
}
方式四:利用__ proto __
function _extends(Parent, Child) {
Child.prototype.__proto__ = Parent.prototype
}
寄生组合式继承保留了借用构造函数+原型链继承的优点,解决了借用构造函数+原型链继承调用两次父类构造函数的缺陷。
三、js实现类的静态方法和静态属性的继承
静态方法定义在构造函数上,例如下面中的Parent类上就具有静态方法eat。
function Child() {}
function Parent() {}
Parent.eat = function () {}
js实现类的静态方法和静态属性的继承也有多种方式。
方式一:
for(let key in Parent) {
console.log('key', key)
//判断是否是父类构造函数上自有的属性
if(Object.prototype.hasOwnProperty.call(Parent, key)) {
console.log('key', key)
Child[key] = Parent[key]
}
}
方式二:
Object.keys(Parent).forEach(key => {
Child[key] = Parent[key]
})
方式三:
Child.__proto__ = Parent
方式四:
Object.setPrototypeOf(Child, Parent)
其中前两种方式是将父类上的静态方法或静态属性复制到子类上,后两种实现方式是修改子类的原型链。
四、ts类继承编译成js源码
下面是一个ts类的继承例子
class Pet {
public name: string
public age: number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
eat() {
console.log('eat food')
}
static run() {
console.log('run')
}
}
class Dog extends Pet {
public favouriteFood: string
constructor(name: string, age: number, favouriteFood: string) {
super(name, age)
this.favouriteFood = favouriteFood
}
eat() {
super.eat()
console.log('eat food:' + this.favouriteFood)
}
}
编译成es5之后,代码为
var __extends = (this && this.__extends) || (function () {
//extendStatics实现了静态方法和静态属性的继承
var extendStatics = function (d, b) {
//利用上面的方式四、方式三和方式一实现静态方法和静态属性的继承
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
//借用辅助函数实现寄生继承
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var Pet = /** @class */ (function () {
function Pet(name, age) {
this.name = name;
this.age = age;
}
Pet.prototype.eat = function () {
console.log('eat food');
};
Pet.run = function () {
console.log('run');
};
return Pet;
}());
var Dog = /** @class */ (function (_super) {
__extends(Dog, _super);
function Dog(name, age, favouriteFood) {
//借用构造函数向父类构造函数传递参数
var _this = _super.call(this, name, age) || this;
_this.favouriteFood = favouriteFood;
return _this;
}
Dog.prototype.eat = function () {
_super.prototype.eat.call(this);
console.log('eat food:' + this.favouriteFood);
};
return Dog;
}(Pet));