关于前端:深入03-继承

导航

[[深刻01] 执行上下文](https://juejin.im/post/684490…
[[深刻02] 原型链](https://juejin.im/post/684490…
[[深刻03] 继承](https://juejin.im/post/684490…
[[深刻04] 事件循环](https://juejin.im/post/684490…
[[深刻05] 柯里化 偏函数 函数记忆](https://juejin.im/post/684490…
[[深刻06] 隐式转换 和 运算符](https://juejin.im/post/684490…
[[深刻07] 浏览器缓存机制(http缓存机制)](https://juejin.im/post/684490…
[[深刻08] 前端平安](https://juejin.im/post/684490…
[[深刻09] 深浅拷贝](https://juejin.im/post/684490…
[[深刻10] Debounce Throttle](https://juejin.im/post/684490…
[[深刻11] 前端路由](https://juejin.im/post/684490…
[[深刻12] 前端模块化](https://juejin.im/post/684490…
[[深刻13] 观察者模式 公布订阅模式 双向数据绑定](https://juejin.im/post/684490…
[[深刻14] canvas](https://juejin.im/post/684490…
[[深刻15] webSocket](https://juejin.im/post/684490…
[[深刻16] webpack](https://juejin.im/post/684490…
[[深刻17] http 和 https](https://juejin.im/post/684490…
[[深刻18] CSS-interview](https://juejin.im/post/684490…
[[深刻19] 手写Promise](https://juejin.im/post/684490…
[[深刻20] 手写函数](https://juejin.im/post/684490…

[[react] Hooks](https://juejin.im/post/684490…

[[部署01] Nginx](https://juejin.im/post/684490…
[[部署02] Docker 部署vue我的项目](https://juejin.im/post/684490…
[[部署03] gitlab-CI](https://juejin.im/post/684490…

[[源码-webpack01-前置常识] AST形象语法树](https://juejin.im/post/684490…
[[源码-webpack02-前置常识] Tapable](https://juejin.im/post/684490…
[[源码-webpack03] 手写webpack – compiler简略编译流程](https://juejin.im/post/684490…
[[源码] Redux React-Redux01](https://juejin.im/post/684490…
[[源码] axios ](https://juejin.im/post/684490…
[[源码] vuex ](https://juejin.im/post/684490…

原型链继承

  • <font color=red>将子类的prototype指向父类的实例,同时批改子类的constructor让其从新指向子类</font>,或者说:将父类的实例作为子类实例的隐式原型
  • 留神点:

    • 在批改了prototype之后,须要从新批改prototype.constructor的指向
  • 毛病:

    • 在创立子类实例的时候,不能向父类传参
    • 不能实现多继承(继承多个父类),因为是给prototype间接赋值
    • 多个实例共享父类的属性和父类原型上的属性,当属性是援用类型时,子类实例间批改会相互影响【特地是数组】
    • 在子类的prototype上挂属性和办法,必须在批改子类的prototype指向之后。
    • Sub.prototype = new Super('woow_wu7')之后Sub.prototype.sex = 'man',不然会被新的援用代替
  • 原理:将子类的prototype指向父类的实例,同时要批改子类的constructor属性让其从新指向子类

    • 因为批改了子类prototype指向父类实例后,子类的prototype.constructor就指向了父类(批改改回来,避免援用出错)
    • 2021/07/24补充
    • 因为:具体是 ( child.constructor ) => ( Child.prototype.constructor ) => ( 父类实例的constructor,即father.constructor ) => ( Father.prototype.constructor ) => Father
    • 所以:当批改了prototype后,Child.prototype.constructor 指向了 Father,所以constructor须要从新指回Child
    • 即:批改了 【 Child.prototype = new Father() 】 之后,须要批改Child.prototype.constructor的指向 【 Child.prototype.constructor = Child 】
  • 毛病:
  • 生成子类实例时,不能向父类传参
  • 不能实现多继承
  • 属性共享,批改子类实例上的原型链上的援用类型的属性时,子类实例会相互影响
  • 在子类的prototype上挂属性和办法时,须要在子类的prototype指向父类的实例之后

    代码示例:
    // 父类
    function Super(name) {
    this.name = name
    }
    Super.prototype.age = 20

    // 子类
    function Sub(address) {
    this.address = address
    }
    Sub.prototype = new Super(‘woow_wu7’) // 原型链继承:将子类的prototype指向父类的实例,子类实例就能拜访父类实例和父类实例原型链上的属性和办法,毛病:不能实现多继承
    Sub.prototype.constructor = Sub // 记得在批改prototype后,须要批改constructor指向,避免援用出错,不批改的话,constructor指向了Super
    Sub.prototype.sex = ‘man’ // 毛病:挂载属性必须在下面步骤之后
    const sub = new Sub(‘hangzhou’) // 毛病:只能向子类传参,不能向父类传参

    console.log(sub.address, ‘子类实例本身属性’)
    console.log(sub.sex, ‘子类实例原型上的属性’)
    console.log(sub.name, ‘子类实例原型上的属性 => 父类实例上的属性’)
    console.log(sub.age, ‘子类实例原型的原型上的属性 => 父类实例原型上的属性’) // 一层层上溯

    批改constructor也能够用上面的形式

    Sub.prototype = Object.create(Super.prototype, {
    // Oject.create第二个参数示意生成的原型上的属性
    // 不要忘了从新指定构造函数
    constructor: {

      value: Student

    }
    })

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

  • 原理:通过call(this, 参数)绑定父类中的this为子类的实例,并执行父类,就相当于把父类中的this换成了子类实例
  • 长处:

    • 能实现多继承(即调用多个父类)
    • 生成子类实例时,能够向父类传参
    • 继承的属性是间接生成在子类实例上的,各个子类实例之间批改援用类型的属性相互不受影响
  • 毛病:

    • 不能继承父类实例对象原型链上的属性和办法

      • (因为父类没有通过new命令生成父类实例,也没有扭转子类prototype的指向,不存在原型链继承)
    • 就是构造函数的毛病,也是长处,作为毛病就是属性和办法都生成在实例上,每次new都会新生成一份,造成系统资源节约(即不共享属性),对于能够共享的只读属性,应该办法原型链上
    借用构造函数继承
    
    
    function Super1(name) {
    this.name = name
    }
    function Super2(age) {
    this.age = age
    }
    Super1.prototype.sex = 'man'
    
    function Sub(name, age, address) {
    Super1.call(this, name) // 通过call,绑定super1的this为子类实例,并执行Super1(),相当于 this.name = name
    Super2.call(this, age) // 长处:能够多继承,同时继承了Super1和Super2中的属性,且在子类实例上批改属性互相不受影响
    this.address = address // 毛病:不能继承父类实例原型链上的属性和办法
    }
    const sub = new Sub('woow_wu7', 20, 'hangzhou') // 长处:能够向父类传参
    console.log(sub)

组合式继承(原型链继承+借用构造函数继承)

  • 组合继承:即原型链继承 + 借用构造函数继承
  • 长处:

    • 既具备借用构造函数继承的长处(向父类传参,多继承,不存在属性共享)
    • 又具备原型链继承的长处(继承父类实例上的属性和父类实例原型链上的属性和办法,并且是共享)
  • 毛病:

    • <font color=red>会调用两次父构造函数,导致子类实例和子类实例原型链上都有同一个属性或办法</font>
    • <font color=red>父类被调用了两次,一次是借用构造函数是的call调用,一次是原型链继承时的new调用</font>
    • <font color=red>因为父类两次调用,所以子类和父类实例原型链上有雷同的属性和办法,造成节约</font>
组合式继承
- 借用构造函数继承 + 原型链继承
- 长处:多继承,将父类传参,某些属性不共享,继承父类实例原型链上的属性和办法
- 毛病:!!!!!! 
    - 父类被调用了两次,一次是借用构造函数是的call调用,一次是原型链继承时的new调用
    - 因为父类两次调用,所以子类和父类实例原型链上有雷同的属性和办法,造成节约
    
    
    
代码:
function Super1(name) {
  this.name = name
}
function Super2(age) {
  this.age = age
}
Super1.prototype.getName = function() {
  return 'Super1' + this.name
}
Super2.prototype.getAge = function() {
  return 'Super2' + this.age
}

function Sub(name, age, address) {
  Super1.call(this, name) // 借用构造函数,多继承,但不能继承原型链上的属性
  Super2.call(this, age)
  this.address = address
}
Sub.prototype = new Super1() 
// 留神:这里没有传参,在原型链继承这条线上,父类实例上的nane属性是undefined
// 留神:原型链继承这条线,还是不能多继承,(如不能同时继承Super1和Super2所在的prototye)因为是间接赋值
Sub.prototype.constructor = Sub // 记得批改constructor指向,从新指回Sub,不然会指向Super1
Sub.prototype.getAddress = function() {
  return 'Sub' + this.address
}

const sub = new Sub('woow_wu7', 20, 'hangzhou')
console.log(sub)



组合继承最大的毛病:
1. 父类执行了两次
  - 1. 在new Sub('woow_wu7', 20, 'hangzhou')是会执行Super.call(this, name)------- 生成一次name // 'woow_wu7'
  - 2. 在Sub.prototype = new Super1() 执行了一次,又会生成一次name // undefined

2020/12/25温习组合式继承

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script>
    // 组合式继承 = 借用构造函数继承 + 原型链式继承
    // 长处:两者组合,互相补充
    // 毛病:
    // 1. 会调用两次父构造函数,导致 (子类实例-即借用构造函数继承 ) 和 ( 子类实例的原型链上-即原型链继承 ) 上都有雷同的属性和办法
    //    - 本例中:子类实例上有 superName1 属性;子类实例的原型链上也有 superName1 属性
    // 2. 父类被调用了两次,一次是借用构造函数是的call调用,一次是原型链继承时的new调用
    // 3. 因为父类两次调用,所以子类和父类实例原型链上有雷同的属性和办法,造成节约
    function Super1(name) {
      this.superName1 = name
    }
    function Super2(name) {
      this.superName2 = name
    }
    Super1.prototype.superAge1 = 10
    Super2.prototype.superAge2 = 20

    function Sub(superName1, superName2, subName) {
      // 借用构造函数继承
      // 长处:能够向父构造函数传参,多继承,属性不共享
      // 毛病:不能继承父类prototype对象原型链上的属性和办法
      Super1.call(this, superName1)
      Super2.call(this, superName2)
      this.subName = subName
    }
    // 原型链继承
    // 长处:能够继承父类实例原型链上的属性和办法,共享属性
    // 毛病:在生成子类实例时不能向父类传传参,不能实现多继承,继承的属性是援用类型时,子类实例之间批改会相互影响
    Sub.prototype = new Super1()
    Sub.prototype.constructor = Sub
    Sub.prototype.subAge = 30

    const sub = new Sub('super1', 'super2', 'sub')
    console.log('sub', sub)
    console.log('sub.superName1', sub.superName1)
    console.log('sub.superName2', sub.superName2)
    console.log('sub.subName', sub.subName)
    console.log('sub.superAge1', sub.superAge1)
    console.log('sub.subAge', sub.subAge)
  </script>
</body>
</html>

寄生组合式继承

  • 寄生组合继承:<font color=red>次要解决了在组合继承中两次调用父类的问题,这导致子类实例的本身属性中有父类实例的属性,子类实例的原型链中也有父类实例原型中的属性</font>

  • 次要解决:

    • 组合式继承中,父类被屡次调用,导致子类实例属性和子类实例原型链上有雷同的属性的问题
    • 因为父类两次被调用,call和new,构造函数中的属性会两次生成,造成资源的节约

    function Super(name) {
    this.name = name
    }
    Super.prototype.getName = function() {
    return ‘Super’ + this.name
    }
    function Sub(name, age) {
    Super.call(this, name) // 借用构造函数
    this.age = age
    }
    // Sub.prototype = new Super() —————- 原型链继承,(没用寄生组合继承之前,即没有应用过渡函数Parasitic)
    function Parasitic(){}
    Parasitic.prototype = Super.prototype
    Sub.prototype = new Parasitic()
    // Parasitic内没有任何属性
    // 这样就没有执行父类(Super构造函数),而是间接的只继承了父类实例原型上的属性
    Sub.prototype.constructor = Sub // 批改prototype要同时批改conscrutor指向
    Sub.prototype.getAge = function() {
    return ‘Sub’ + this.age
    }
    const sub = new Sub(‘woow_wu7’, 20)
    console.log(sub)

2020/12/25温习寄生组合继承

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script>
    // 寄生组合式继承
    function Super1(name) {
      this.superName1 = name
    }
    function Super2(name) {
      this.superName2 = name
    }
    Super1.prototype.superAge1 = 20
    function Sub(superName1, superName2, name) {
      Super1.call(this, superName1)
      Super2.call(this, superName2)
      this.subName = name
    }
    function Parasitic() { } // 两头函数,自身没有任何属性和办法
    Parasitic.prototype = Super1.prototype
    // 这样 sub 实例就能继承 Super1.prototype上的属性和办法,而这条继承线不必在继承 super1 实例上的办法
    Sub.prototype = new Parasitic()
    Sub.prototype.constructor = Sub
    Sub.prototype.subAge = 30
    const sub = new Sub('super1', 'super2', 'sub')
    console.log('sub', sub)
  </script>
</body>
</html>

class

  • class能够通过 <font color=red>extends</font>关键字实现继承
  • 子类必须在constructor办法中调用<font color=red>super办法</font>,否在新建实例时会报错

    • 因为子类的this须要通过父类的构造函数获取,不调用super办法就得不到this对象
  • 子类没有定义constructor会被默认增加
  • <font color=red>在子类的constructor中必须先调用super()后能力应用this</font>
  • <font color=red>父类的静态方法也会被子类所继承</font>

  • es5的借用构造函数式继承:
  • 是先创立子类的this,而后将父类的属性和办法帮到子类的this对象上

    es6的继承:

  • 是将父类实例的属性和办法增加到this上,而后用子类的构造函数批改this

super关键字

  • 能够作为函数,也能够作为对象

super作为函数

  • <font color=red>super作为函数只能用于构造函数中,示意父类的构造函数,this指向子类的实例</font>
  • 留神:
    <font color=red>super作为函数,尽管示意父类的构造函数,但返回的是子类的实例 ,即super外部的this指向的是子类的实例!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!</font>

  • super作为函数:只能用于构造函数中,示意父类的构造函数
  • super作为函数:外部的this指向的是子类的实例

    class A {
    constructor() {
    console.log(this, ‘this’)
    }
    }
    class B extends A {
    constructor() {
    super() // 留神:super最为函数,只能用于构造函数中示意父类的构造函数,外部this指向子类的实例
    }
    }

    new B() // B this ========> super作为函数,外部this指向子类的实例

super作为对象

  • super作为对象
  • <font color=red>在一般办法中:指向父类的原型,this指向以后子类的实例</font>(实例上的属性和办法无奈通过该super获取)
  • <font color=red>在静态方法中:指向父类,this指向子类</font>

    
    
    因为this指向子类实例,所以如果通过super对某个属性赋值,这时super就是this,赋值的属性会变成子类实例的属性。
    
    class A {
    constructor() {
      this.x = 1;
    }
    }
    
    class B extends A {
    constructor() {
      super();
      this.x = 2;
      super.x = 3; // !!!!!super对某个属性赋值,super就示意this,即子类的实例!!!!!
      console.log(super.x); // undefined // super在一般函数中是对象时,示意父类的原型
      console.log(this.x); // 3
    }
    }
    
    let b = new B();
    
    
    super作为对象,在静态方法中:示意父类
    
    class Parent {
    static myMethod(msg) {
      console.log('static', msg);
    }
    
    myMethod(msg) {
      console.log('instance', msg);
    }
    }
    
    class Child extends Parent {
    static myMethod(msg) {
      super.myMethod(msg); // super作为对象,在静态方法中,示意父类,调用父类的静态方法myMethod
    }
    myMethod(msg) {
      super.myMethod(msg);
    }
    }
    
    Child.myMethod(1); 
    // static 1
    // Child.myMethod()是调用Child的静态方法,静态方法中的super对象示意父类
    
    var child = new Child();
    child.myMethod(2); 
    // instance 2
    // 实例上调用myMethod,没构造函数中没有,就去原型上查找,super对象在一般办法中示意父类的原型

super总结

  • <font color=red>super作为函数,示意父类的构造函数,this指向子类实例(此时this只能用于)</font>
  • <font color=red>super作为对象,在一般办法中,示意父类的原型,this指向子类实例</font></font>
  • <font color=red>super作为对象,在静态方法中,示意父类,this指向子类</font>

es6继承

  • class作为构造函数的语法糖,同时具备__proto__ 和 prototype
  • 所以 class 同时具备两条继承链:

    • 子类的__proto__总是指向父类(示意构造函数的继承)
    • 子类的prototype.__proto__总是指向父类的prototype(示意办法的继承)

我的简书:https://www.jianshu.com/p/d88…
川神:https://juejin.im/post/684490…
https://www.jianshu.com/p/a88…

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理