JavaScript手写typescript的继承

171 阅读3分钟

JavaScript原型基础

声明一个函数,函数会自动生成两个属性一个是constructor 属性指向构造函数,另一个是prototype属性指向原型对象。 实例化的对象是没有prototype属性的,会有有两个属性,一个是contructor 指向构造函数本身,另一个是__proto__属性指向构造器的prototype属性。

继续方式有几种

  1. 原型链继承 原型链继承的不足是:不能通过子类构造函数向父类构造器传递参数。

  2. 借用构造继承,这种方式解决了子类不能向父类构造器传递参数的局限,这种方式通过在子类中使用apply或者call改变父构造函数this来实现。

  3. 借用构造原型继承组合方式 缺点是调用了两次构造函数,实际上是在子类构造的时候调用了一次构造函数是在子类的原型上,在实例实话的时候借用的时候调用了一次。

    3.1. 进入Child构造函数为属性赋值,分配内存空间,浪费内存;

    3.2. 赋值导致导致效率下降,关键字new Child赋值无意义,出现代码冗余,赋值操作是通过子类构造函数的借用call来对父类构造函数进行赋值的

  4. 寄生组合继承实现, 寄生继承的组合实现方式,

原型链继承方式

缺点:

  1. 不能向父类构造函数传参
function Parent (name, age, sex){
    this.name = name
    this.age = age
    this.sex = sex
}
function Child (phone) {
    this.phone = phone
}
Child.prototype = new Parent()
Child.constructor = Child


借用构造函数

优点

  1. 解决了可以向父类构造函数传参

缺点

  1. 调用了两次构造函数
function Parent (name, age, sex){
    this.name = name
    this.age = age
    this.sex = sex
}
function Child (name, age, sex, phone) {
    Parent.call(this, name, age, sex, phone)
    this.phone = phone
}

组合继承(借用构造与原型链方式)

优点

  1. 解决了原型链不能给父类传值的缺点
  2. 解决了子类不能调用父类方法原型上的方法

缺点

  1. 调用了两次构造函数,导致内存浪费,代码冗余
function Parent (name, age, sex){
    this.name = name
    this.age = age
    this.sex = sex
}
Parent.prototype.getname = function () {
    return this.name;
}
function Child (ame, age, sex, phone) {
    Parent.call(this,ame, age, sex, phone)
    this.phone = phone 
}
Child.prototype = new Parent()
Child.prototype.constructor = Child

寄生组合方式

第一种方式:

function Parent (name, age, sex){
    this.name = name
    this.age = age
    this.sex = sex
}
Parent.prototype.getname = function () {
    return this.name;
}
fucntion Middle () {

}
//这里把middle prototype 的原型对象指向了父类的原型对象空间,但是contructor 也发生了变化,变成了Parent
Middle.prototype = Parent.prototype
const middle = new Middle()

middle.prototype.constructor = Child
function Child (ame, age, sex, phone) {
    this.phone = phone
}

Child.prototype = Parent.prototype
Child.prototype.constructor = Child

封装一下

function Parent (name, age, sex){
    this.name = name
    this.age = age
    this.sex = sex
}
Parent.prototype.getname = function () {
    return this.name;
}
fucntion Middle () {

}
//这里把middle prototype 的原型对象指向了父类的原型对象空间,但是contructor 也发生了变化,变成了Parent
Middle.prototype = Parent.prototype
const middle = new Middle()
// 所以Child的原型对象指向middle的原型对象就是指向的Parent的原型对象了。此时Child.prototype.constructor === Parent, 所以要把当前对象的constructor 指向自己
Child.prototype = middle.prototype
指向构造函数本身,就是Child
function Child (ame, age, sex, phone) {
    this.phone = phone
}
Child.prototype = Parent.prototype
Child.prototype.constructor = Child

//extends 方法的封装
function _extends(Sup, Sub) {
    function Middle() {}
    Middle.prototype = Sup.prototype
    const middle = new Middle()
    return middle
}
Child.prototype = _extends(Parent, Child);
Child.prototype.constructor = Child;

第三种方法

function Parent (name, age, sex){
    this.name = name
    this.age = age
    this.sex = sex
}
Parent.prototype.getname = function () {
    return this.name;
}
function _extends(Sup, Sub) {
    const middle = Object.create(Sup.prototype)
    Sub.prototype = middle
    Sub.prototype.constructor = Sub
}
//这里是需要给父类传递参数的
function Child (name, age, sex, phone) {
    Parent.call(this, name, age, sex)
    this.phone = phone
}
_extends(Parent, Child)
let child = new Child('xb', 18, 'male', 13812345678)