JavaScript 进阶营让前端飞

【重学】继承

2019-06-25  本文已影响0人  woow_wu7

注意:文中错误的地方已经在掘金的文章中修改了(简书这里其实也改了的)
https://juejin.im/editor/drafts/5e1fc5b251882520a06362bb

(1)原型链继承

(1) 原型继承 - 将父类的实例作为子类的原型


// 父类
function Super(name) {
  this.name = name
}
Super.prototype.getAddress = function() {
  return 'chongqing'
}
// 子类
function Sub(age) {
  this.age = age
}
Sub.prototype = new Super('woow-wu') -------- 将父类的实例作为子类实例的原型,子类的实例就可以访问父类实例和父类原型上的属性和方法
Sub.prototype.getSex = function() {
  return 'man'
}
const sub = new Sub(20)
console.log(sub.name) ------------------- 先在sub实例上找name,未找到再在super实例上查找,未找到再在super原型上查找 // 'woow-wu'
console.log(sub.getAddress()) // 'chongqing'
console.log(sub.age) // 20
console.log(sub.getSex()) // man

(2)借用构造函数继承(经典继承)

function Super1 (name) {
  this.name = {name: name}
}
function Super2 (sex) {
  this.sex = sex
}
Super.prototype.getAddress = function() {
  return 'chongqing'
}
function Sub(name, sex) {
  Super.call(this, name)  -------------- 直接调用Super类(函数),将Super中的this绑定在sub实例上,并传参给父类
  Super2.call(this, sex)
}
const sub1 = new Sub('woow-wu', 'man')  ------------------------------ 可以向父类传参
console.log(sub1) // {name: {…}, sex: "man"}
const sub2 = new Sub('wang', 'woman')
console.log(sub2) // {name: {…}, sex: "woman"} ----------------------- 可以实现多继承,且实例之间的属性互不影响

(3)组合式继承

原型链继承和借用构造函数继承的组合

function Super(name) {
  this.name = name
  this.score = 100
}
Super.prototype.getName= function() {
  return 'super' + this.name
}
function Sub(name, dress) {
  Super.call(this, name)
  this.address = address
}
Sub.prototype = new Super() // 注意,这里没有传参,在原型链继承这条线上,父类实例的原型上的nane属性是undefined
Sub.ptototype.getSex = function() {
  return 'man'
}
cosnt sub = new Sub('woo-wu', 'chongqing')
console.log(sub)



组合继承最大的缺点:
1. 父类执行了两次
  - 1. 在new Sub('woo-wu', 'chongqing')是会执行Super.call(this, name)------- 生成一次name,score
  - 2. 在Sub.prototype = new Super() 执行了一次,又会生成一次name,score

(4)原型式继承

const obj = {
  name: 'wang',
  frends: ['li', 'go']
}
obj.__proto__ .age = 20
function createObj(obj) {
  function F(){}
  F.prototype = obj
  return new F()
}

const a = createObj(obj)
const b = createObj(obj)

a.name = 'xxxxx' 
// ----------------- 这样实际上是直接在a对象上添加了name属性,而不是修改a原型上的属性, 原型上的属性值不能直接通过实例来修改
// ----------------- 如果要修改,需要通过 a.__proto__.name = 30这样来修改
  console.log(a.name) // xxxxx
  console.log(b.name) // 'wang'
  console.log(a.age)  // 20

(5)寄生组合式继承

寄生组合式继承
如图4



组合式继承存在的问题:
1. 父类构造函数执行了两次
  - 在借用构造函数式的时候执行了1次
  - 在原型继承的时候执行了1次
  - 所以子类实例上有父类中的属性和方法,子类的原型对象(即父类的实例)上也有父类的属性和方法



function Father (name) {
  this.name = name
}
Father.prototype.addressFather = 'china'

function Child(name, sex) {
  Father.call(this, name) //----------------------------------------- 借用构造函数继承
  this.sex = sex
}
// Child.prototype = new Father() //------------------------------ 原型继承

function F(){}
F.prototype = Father.prototype
Child.protype = new F() --------------- 这样就没有执行父类(构造函数),而是间接只继承了父类的原型

Child.prototype.addressChild = 'shanghai'

const child = new Child('wang', 'man')
console.log(child)
console.log(child.name) // wang
console.log(child.__proto__.name) 
图4
组合继承: 下图 图5
function Father (name) {
  this.name = name
}
Father.prototype.addressFather = 'china'

function Child(name, sex) {
  Father.call(this, name) //----------------------------------------- 借用构造函数继承
  this.sex = sex
}
Child.prototype = new Father() //------------------------------ 原型继承
Child.prototype.addressChild = 'shanghai'

const child = new Child('wang', 'man')
console.log(child)
console.log(child.name) // wang
console.log(child.__proto__.name) ----------------------------- undefined,因为new Father()时没有传参
图5

https://www.jianshu.com/p/a8844b28ff79

https://juejin.im/post/591523588d6d8100585ba595










(6) es6中的继承

1. 作为对象,------------------------------ A.__proto__ = B
2. 作为构造函数,-------------------------- A.prototype.__proto__ = B.prototype
来源网络








(7) es6中的继承 (其他)

new.target

    class Father {
      constructor() {
        console.log(new.target.name)
      }
    }
    class Child extends Father {
      constructor() {
        super() ------------------- super作为函数,只能用于构造函数,表示父类的构造函数
                ------------------- 但是super内部的this指代的是子类的实例
      }
    }

    const fahter = new Father() 
    // Father

    const child = new Child()  
    // Child ------ 因为super虽然是借用了父类的构造函数,但是this绑定在子类的实例上,(类似借用构造函数继承)

    new.target用在函数内部,如果该函数通过new命令调用,new.target指正在执行的函数

super关键字(函数 或者 对象)

    class A {
      constructor() {
        this.name = 'woow-wu'
      }
      go() {
        console.log('home')
      }
    }
    class B extends A {
      constructor() {
        super()  --------------------  super作为函数,只能用于构造函数,表示父类的构造函数,但this指向子类实例
        super.go() ------------------  super作为对象,在普通函数中指 父类的原型 ---- 所以无法继承父类实例上的属性和方法
      }
      getName() {
        return super.name
      }
    }
    cosnt b = new B()
    b.getName ----------------------------- undefined,父类实例上的name并没有被继承,super指的是父类的原型
super作为对象,表示父类的原型,内部的this指的是子类的实例
super作为函数,表示父类的构造函数,内部的this指的是子类的实例,(作为函数,只能用于构造函数中)



class A {
      constructor() {
        this.name = 'wang'
      }
      getAName() {
        return this.name
      }
    }

    class B extends A {
      constructor() {
        super()
        this.name = 'li'
      }
      getBName() {
        return super.getAName()
      }
    }
    const b = new B()
    const res = b.getBName()
    console.log(res) // li
super作为对象,在静态方法中,指向父类
super作为对象,在普通方法中,指向父类的原型



class Father {
      constructor() {}
      setAge(age) {
        console.log(age * 100)
      }
      static setAge(age) {
        console.log(age)
      }
    }
    class Child extends Father {
      constructor() {
        super()
      }
      getAge(age) {
        super.setAge(age) // 通过实例调用,super在普通方法中,表示父类的原型
      }
      static getAge(age) {
        super.setAge(age) // 静态方法中的super对象,指的是父类,父类直接调用的是静态方法setAge
      }
    }
    const child = new Child()
    Child.getAge(10) // 10     静态方法直接通过类直接调用
    child.getAge(10) // 1000 通过实例调用的是类原型上的方法
上一篇 下一篇

猜你喜欢

热点阅读