【重学】继承

  • 时间:2020-04-24 15:23 作者:woow_wu7 来源: 阅读:465
  • 扫一扫,手机访问
摘要:(1)原型链继承将父类的实例作为子类实例的原型缺点在创立子类实例时,不能向父类传参不能实现多继承(继承多个父类)多个实例共享父类的属性,当父类属性是引用类型时,子类实例之间修改会相互影响,【特别是数组】要在子类prototype上挂方法,必需在子类实例的隐式原型指向父类实例之后(Sub.protot

(1)原型链继承

  • 将父类的实例作为子类实例的原型
  • 缺点
    • 在创立子类实例时,不能向父类传参
    • 不能实现多继承(继承多个父类)
    • 多个实例共享父类的属性,当父类属性是引用类型时,子类实例之间修改会相互影响,【特别是数组】
    • 要在子类prototype上挂方法,必需在子类实例的隐式原型指向父类实例之后
      (Sub.prototype = new Super('woow-wu')之后Sub.prototype.getSex
(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) // 20console.log(sub.getSex()) // man

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

  • 优点
    • 在创立子类实例时,可以向父类传参
    • 继承的属性是直接生成在子类实例上的,子类实例继承的属性相互不受影响(并没有继承父类的原型)
    • 可以实现多继承(即调用多个父类)
  • 缺点
    • 不能继承父类原型链上的属性和方法(由于没有new Super,即根本没有new父类,不存在原型链继承)
    • 就是构造函数的缺点,也是优点,作为缺点就是属性和方法都生成在实例上,每次new都会新生成一份,造成系统资源白费(即不共享属性),对于可以共享的只读属性,应该方法原型链上
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属性是undefinedSub.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)原型式继承

  • 就是Object.create()的模拟实现
const obj = {  name: 'wang',  frends: ['li', 'go']}obj.__proto__ .age = 20function 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.prototypeChild.protype = new F() --------------- 这样就没有执行父类(构造函数),而是间接只继承了父类的原型Child.prototype.addressChild = 'shanghai'const child = new Child('wang', 'man')console.log(child)console.log(child.name) // wangconsole.log(child.__proto__.name) 
图4
组合继承: 下图 图5function 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) // wangconsole.log(child.__proto__.name) ----------------------------- undefined,由于new Father()时没有传参
图5

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

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










(6) es6中的继承

  • class作为构造函数的语法糖,同时具备__proto__prototype,由于函数是函数也是对象
  • 所以class同时具备两条继承链
    • 子类的__proto__总是指向父类 (表示构造函数的继承)
    • 子类的prototype.__proto__总是指向父类的prototype(表示方法的继承)
1. 作为对象,------------------------------ A.__proto__ = B2. 作为构造函数,-------------------------- A.prototype.__proto__ = B.prototype
来源网络








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

  • calss 通过 extends关键字实现继承
  • 父类的静态方法也会被子类继承
  • Object.getPrototype(a) === b 可以用来从子类上获取父类

new.target

  • 函数内部可以使用new.target,假如是new命令调用函数,new.target指向当前正在执行的函数,否则是undefined
    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关键字(函数 或者者 对象)

  • super作为函数:
    • super作为函数时,表示父类的构造函数(super作为函数时,只能用在构造函数中)
    • super作为函数时,----------- 返回的是子类的实例,super内部的this指的是子类的实例
  • super作为对象:
    • 在普通方法中 ------------------ 指向父类的原型,同样this指向子类实例(和super作为函数时一样)
    • 在静态方法中 ------------------ 指向父类,内部的this指向子类,而不是子类的实例
    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 通过实例调用的是类原型上的方法
  • 全部评论(0)
最新发布的资讯信息
【系统环境|】从谷歌到手机厂商都下决心了,要清除32位应用这匹“害群之马”(2025-10-17 05:41)
【系统环境|】Windows上使用QEMU创建aarch64(ARM64)虚拟机(2025-10-17 05:40)
【系统环境|】nodejs 如何安装在aarch64平台(2025-10-17 05:39)
【系统环境|】常用git命令-从远程更新代码合并分支、提交代码等(2025-10-17 05:38)
【系统环境|】技术干货|常用的 Git 功能和选项(2025-10-17 05:38)
【系统环境|】掌握git命令,图解一目了然(2025-10-17 05:37)
【系统环境|】总结几个常用的Git命令的使用方法(2025-10-17 05:36)
【系统环境|】这篇 Git 教程太清晰了,很多 3 年经验程序员都收藏了(2025-10-17 05:35)
【系统环境|】Git常用命令及操作指南(2025-10-17 05:35)
【系统环境|】「实用」盘点那些开发中最常用的Git命令(2025-10-17 05:34)
手机二维码手机访问领取大礼包
返回顶部