聊聊js中的继承

232 阅读1分钟

js是可以面向对象的,自然少不了继承,js实现继承的方式有好几种,这里做一些记录

原型继承

function Super(name){
    this.name = name
    this.color = ['red','blue']
}
Super.prototype.addColor = function(color){
    this.color.push(color)
}
function Sub(){}
Sub.prototype  = new Super()
Sub.prototype.constructor = Sub

let sub1 = new Sub('mike')
let sub2 = new Sub('jake')
console.log(sub1.color) // ['red','blue']
sub2.addColor('black')
console.log(sub1.color) // ['red','blue','black']

原型继承的原理就是让子类的原型成为父类的示例,这样子类就可以共享父类的方法,需要注意重新给constructor赋值,这种继承的缺点有两个:

  • 1 无法在实例化子类时自定义构造参数
  • 2 子类实例共享引用类型属性,如上所示,sub2改变color会影响sub1

构造函数继承

function Super(name){
    this.name = name
    this.sayName = function(){ console.log(this.name)}
}

function Sub(name){
    Super.call(this,name)
}

let sub = new Sub('mike')
sub.sayName() // mike

可以看到,构造函数继承就是将父类函数在子类中执行一遍,此时引用类型属性不再共享,构造参数可自定义,可缺点也很明显,每个子类重新创建了父类的方法,不存在共享。

组合继承

结合以上两种方法便有了组合继承

function Super(name){
    this.name = name
    this.color = ['red','blue']
}
Super.prototype.addColor = function(color){
    this.color.push(color)
}
function Sub(name,age){
    Super.call(this,name)
    this.age = age
}
Sub.prototype  = new Super()
Sub.prototype.constructor = Sub
let sub1 = new Sub('mike')
let sub2 = new Sub('jake')
console.log(sub1.color)  // ['red','blue']
sub2.addColor('black')
console.log(sub1.color)  // ['red','blue']

父类方法在子类中共享,引用属性不共享,构造参数可自定义,ok了?这里还有一个问题,这种方法执行了两次父类构造函数,如果父类的构造函数代码特别多就不太好了。

寄生组合继承

function Super(name){
    this.name = name
    this.color = ['red','blue']
}
Super.prototype.addColor = function(color){
    this.color.push(color)
}
function Sub(name,age){
    Super.call(this,name)
    this.age = age
}
Sub.prototype  = Object.create(Super.prototype)
Sub.prototype.constructor = Sub

通过object.create方法直接将Sub.prototype链到Super.prototype实现方法继承又不必执行一次父类构造函数,Object.create可以这样实现:

Object.create = function(obj){
    function F(){}
    F.prototype = obj
    return new F()
}