【重学】继承

作者: woow_wu7 | 来源:发表于2019-06-25 10:07 被阅读0次

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

(1)原型链继承

  • 将子类的protoytpe指向父类的实例(将父类的实例作为子类实例的隐式原型)
  • 或者说:将父类实例作为子类的显示原型
  • 缺点
    • 在创建子类实例时,不能向父类传参
    • 不能实现多继承(继承多个父类)
    • 多个实例共享父类的属性,当父类属性是引用类型时,子类实例之间修改会相互影响,【特别是数组】
    • 要在子类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) // 20
console.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属性是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)原型式继承

  • 就是Object.create()的模拟实现
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中的继承

  • class作为构造函数的语法糖,同时具有__proto__prototype,因为函数是函数也是对象
  • 所以class同时具有两条继承链
    • 子类的__proto__总是指向父类 (表示构造函数的继承)
    • 子类的prototype.__proto__总是指向父类的prototype(表示方法的继承)
1. 作为对象,------------------------------ A.__proto__ = B
2. 作为构造函数,-------------------------- 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 通过实例调用的是类原型上的方法

相关文章

  • 【重学】继承

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

  • [Java]重学Java-继承

    复用 随着码代码的时间增长,程序员会越发需要"复用"这个能力,比如最简单的对String类进行判空: 我们需要每次...

  • 重学 JavaScript 笔记(二)—— 原型 && 继承

    3.1 函数 3.1.1 函数声明: 3.1.2 函数调用 3.1.3 变量作用域 函数内声明的变量,只在该函数内...

  • 【重学前端】JavaScript中的继承

    JavaScript中继承主要分为六种:类式继承(原型链继承)、构造函数继承、组合继承、原型式继承、寄生式继承、寄...

  • 重学es5构造函数 类与继承

    ES6+已经在前端市场流行很多年了,似乎都快忘了以前常用的es5的一些特性,今天回顾一些es5的构造函数。 我现在...

  • 感受英国的教育

    英国的教育注重学生自主的发展,注重学生兴趣的培养,注重学生实际能力的锻炼。为此学校教育的各个环节都注...

  • 知识分子学佛的通病

    能知不能行 重学理不重实证

  • 重学PHP

    Run your application: Read the documentation at https://s...

  • 重学pet

    刚知道这一期的觉知课程主题是沟通时,顿时就产生了不去的念头,可是因为怕麻烦,还是决定跟下去。在课堂上,明显觉得自己...

  • 重学HTML

    为什么要重学呢?因为感觉回到不出来这些问题: HTML是什么,HTML5是什么 HTML元素标签、属性都是什么概念...

网友评论

    本文标题:【重学】继承

    本文链接:https://www.haomeiwen.com/subject/eyagqctx.html