js 继承方式

288 阅读3分钟

一、原型链继承

function Parent(name){
    this.name = name;
    this.arr = [1]
}
Parent.prototype.say = function(){
    console.log('hello')
}
function Child(like){
    this.like = like
}
Child.prototype = new Parent()//核心,此时Child.prototype.constructor = Parent,修改指向
Child.prototype.constructor = Child;
let a = new Child()
let b = new Child()
 核心:将父类的实例作为子类的原型
 优点:由于方法定义在父类的原型上,复用了父类构造函数的方法
 缺点:
      1、创建子类实例的时候,不能传父类参数
      2、子类实例共用了父类构造函数的引用属性
      3、无法实现多继承

二、借用构造函数继承

function Parent(name){
    this.name = name;
    this.arr = [1];
    this.say = function(){
        console.log('hello')
    }
}
function Child(name, like){
    Parent.call(this, name)//核心,拷贝了父类的实例属性和方法
    this.like = like
}
let a = new Child('zhangsan', 'apple')
let b = new Child('lisi', 'orange')
核心:借用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类
优点:各实例之间独立
     - 创建子类实例的时候,可以向父类传参
     - 子类实例不共用父类构造函数的引用属性
     - 可实现多继承
缺点:父类的方法不能共用(由于方法在父类构造函数中定义,所以每创建一次实例就要创建一次方法)
     子类实例继承不了父类原型上的属性(因为没有用到原型)

三、组合继承

function Parent(name){
    this.name = name;
    this.arr = [1]
}
Parent.prototype.say = function(){
    console.log('hello')
}
function Child(name, like){
    Parent.call(this, name, like)
    this.like = like
}
Child.prototype = new Parent()
Child.prototype.constructor = Child
let a = new Child('zhangsan', 'apple')
let b = new Child('lisi', 'orange')
核心:通过调用父类的构造函数,继承父类的属性并保存传参的优点,然后通过将父类的实例作为子类的原型,实现函数复用
优点:- 保留构造函数的优点:创建子类实例可以向父类构造函数传递参数
     - 保留原型链的优点:父类的方法定义在父类的原型对象上,可以实现方法复用
     - 不共享父类的引用属性
缺点:由于调用了两次父类的构造方法,会存在一份多余的父类实例属性
     第一次Parent.call(this)从父类拷贝一份父类实例属性,作为子类的实例属性,第二次Child.prototype = new Parent();
     创建父类实例作为子类原型,Child.prototype中的父类属性和方法会被第一次拷贝来的实例属性屏蔽掉,所以多余

四、组合继承优化

function Parent(name){
    this.name = name;
    this.arr = [1]
}
Parent.prototype.say = function(){
    console.log('hello')
}
function Child(name, like){
    Parent.call(this, name, like)
    this.like = like
}
Child.prototype = Parent.prototype
Child.prototype.constructor = Child
let a = new Child('zhangsan', 'apple')
let b = new Child('lisi', 'orange')
let p1 = new Parent('wangwu')
核心:通过这种方式砍掉父类的实例属性,这样在调用父类的构造函数的时候,就不会初始化两次实例,避免组合继承的缺点
优点:- 只调用一次父类构造函数
     - 保留构造函数的优点:创建子类实例,可以向父类构造函数传参
     - 保留原型链的优点: 父类的实例方法定义在父类的原型对象上,可以实现方法复用
缺点: 修正构造函数指向之后,父类实例的构造函数指向,同时也发生变化
注意: 要记得修复Child.prototype.constructor指向
      原因是:不能判断子类实例的直接构造函数,到底是子类构造函数还是父类构造函数

五、寄生组合继承(完美)

function Parent(name){
    this.name = name;
    this.arr = [1]
}
Parent.prototype.say = function(){
    console.log('hello')
}
function Child(name, like){
    Parent.call(this, name, like)
    this.like = like
}
//核心 通过创建中间对象,子类原型和弗雷原型就会隔离开,不是同一个,有效地避免了方式4的缺点
Child.prototype = Object.create(Parent.prototype)
//修复构造函数指向
Child.prototype.constructor = Child
let a = new Child('zhangsan', 'apple')
let b = new Child('lisi', 'orange')
let p = new Parent('wangwu')