javascript中继承的方法和理解

本文详细介绍了JavaScript中实现继承的几种常见方法,包括原型继承、借用构造函数继承、组合继承、拷贝继承、寄生式继承和寄生组合继承,分析了各自的优缺点。此外,还探讨了ES6的类继承语法,指出其在提高代码复用性和维护性方面的优势,并强调在使用继承时要考虑类之间的耦合关系和多态前提。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

继承

关于继承简单的理解就是在子类中想要使用父类的属性和方法,那么就需要子类来继承父类的属性和方法。

  • 那就涉及到构造函数,因为构造函数可以创建一堆有属性有方法合理的对象,书写 构造函数的时候属性直接书写在构造函数体内,方法书写在构造函数的原型(prototype)上,为了给所有的实例使用,而且创建出来的每一个实例都可以使用。

了解完继承的大致思维后,就需要去实现它,那么就出现了很多思路,又因为javascript的灵活性,所以就出现了很多的继承方法,在这里简单介绍几个,还有es6中继承的使用。

原型继承

  • 思想:利用修改原型链的方式来达到继承的效果
    原型集成的优点和缺点
    • 优点: 属性和方法都继承下来的
    • 缺点:
      1. 在做 Student 的实例, 一个构造函数需要在两个位置传递参数
      2. 我继承来的属性, 不在自己身上
function Person(name) {
      this.name = name
    }
    Person.prototype.sayHi = function () { console.log('Person') }

  1. Person 的实例是什么样子的
       {
            name: 'xxx',
            __proto__: Person.prototype {
              constructor: Person,
              sayHi: function () {},
              __proto__: Object.prototype
            }
          }
  1. 如果我想让 s1 在现在的基础上添加一个 name 属性和 sayHi 方法
    • 在 s1 的 proto 里面只要加上 name 属性和 sayHi 方法
    • 因为当你访问 s1 的成员的时候, 如果自己没有, 会自动去 proto 上查找
    • 如果 proto 上没有, 再去 proto 上找
  2. s1 的 proto 是 Student.prototype
    • 我只要修改了 Student.prototype 那么就可以实现了

借用构造函数继承 / call 继承 / 借用继承

  • 利用父类构造函数体 和 call 方法来达到继承效果
    1. 构造函数也是一个函数
      => 可以不和 new 关键字连用
      => this 不是指向当前实例
      => 只要是执行的时候, this 指向谁, 就是像谁身上添加成员
    2. call 是一个可以在函数后面调用的方法
      => 所有函数都可以调用
      => 构造函数也是函数, 构造函数也可以调用 call 方法
      => 用来改变函数内部的 this 指向
      => 第二个参数开始一次给函数内传递参数
    3. 可以把父类当做普通函数使用
      => 通过 call 方法, 把父类函数内的 this 指向子类的实例
      => 父类里面添加的属性就是添加在子类的实例身上了

借用继承的优点和缺点

  • 优点:
    • new 一个实例的时候, 在一个位置传递参数
    • 继承来的属性出现在自己的身上
  • 缺点:
    • 父类 prototype 身上的内容不能继承下来
//父类
function Person(name) {
     this.name = name
   }
   Person.prototype.sayHi = function () { console.log('Person') }

  // 利用 call 方法来调用一下构造函数
    var obj = {
      a: 100
    }
    // 本次调用 Person 函数, 就是把函数内部的 this 改成了 obj
    // 'jack' 就是传递给 name 形参的数据
    Person.call(obj,'Jack')
    console.log(obj)

组合继承

  • 字面理解即可指 原型继承 和 借用继承 组合在一起的继承方案
  • 同时使用 借用继承 和 原型继承
    缺点:
    1. 属性重复了
    2. 方法太远了
//父类
function Person(name) {
     this.name = name
   }
   Person.prototype.sayHi = function () { console.log('Person') }
 function Student(age, name) {
      this.age = age

      // 使用借用继承
      Person.call(this, name)
    }

    // 使用原型继承继承方法
    Student.prototype = new Person()

    const s1 = new Student(15, "Jack")
    console.log(s1)

拷贝继承 / for in 继承

  • 利用 for in 循环遍历父类的实例来达到继承效果
    1. 父类的实例是存在属性和方法的
      => 就可以使用 forin 循环进行遍历
      => 在遍历的过程中, 不管自己本身的属性会被遍历出来
      => 原型上的方法也会被遍历出来
    2. 子类的原型本身是一个对象
      => 我可以给他赋值一个新的对象
      => 把父类实例里面的属性和方法全部都复制一份上去
   function Student(age) {
      this.age = age
    }
    Student.prototype = {}
    // 把父类实例里面的所有内容遍历出来, 直接放在子类的 原型上
    const p = new Person('Jack')
    for (let key in p) {
      Student.prototype[key] = p[key]
    }

    const s1 = new Student(15)
    console.log(s1)

寄生式继承

  • 因为他是以 父类的实例 冒充 子类的实例所以叫寄生式继承
    • 构造函数内部不要写 return
      => 当你 return 一个复杂数据类型的时候
      => 构造函数本身创建的对象就没有了, 你 return 什么就是什么
 //父类
function Person(name) {
      this.name = name
    }
    Person.prototype.sayHi = function () { console.log('Person') }
    function Student(name) {
      this.a = 100
      Person.prototype.fn = function () {}
      // 拿到父类的实例
      return new Person(name)
    }

    // 看似你拿到的是 Student 的实例
    // 因为你在构造函数体内书写了 return 复杂数据类型
    // 所以你得到的本身的 Student 的实例不存在了
    // 真实得到的内容就是 Perosn 的实例
    const s1 = new Student('Jack')
    console.log(s1)

寄生式组合继承

  • 指 借用继承 和 拷贝继承 一起使用
  • 并且在拷贝继承中, 指去拷贝父类的原型, 不拷贝父类的实例
    => 因为父类实例身上自己本身有的内容
    => 被借用继承的时候, 直接继承在了子类的身上
    缺点:
    1. for in 循环
    2. 当你给子类添加独立方法的时候, 和父类继承来的方法分不开
 //父类
function Person(name) {
      this.name = name
    }
    Person.prototype.sayHi = function () { console.log('Person') }
    function Student(age, name) {
      this.age = age
      // 借用继承
      // 只要书写在父类构造函数体内的内容都能继承下来
      // 只是父类 prototype 上的内容继承不下来
      Person.call(this, name)
    }

    // 利用拷贝继承的一部分, 来实现继承父类的原型
    for (let key in Person.prototype) {
      // 把父类原型上的所有内容添加到子类原型身上
      Student.prototype[key] = Person.prototype[key]
    }

    Student.prototype.run = function () {
      console.log('跑')
    }

    const s1 = new Student(15, 'Rose')
    console.log(s1)

ES6 的类继承语法

  • 在 ES6 语法标准里面, 有一个继承语法
  • 为了实现 类的继承
语法:
       class 子类 extends 你要继承的父类 {
         constructor () {
           // 为了继承父类的属性
           super()
         }
       }

注意:

  1. 你在书写 super 的时候, 必须写在你自己的属性上面
    • 先继承父类的属性, 再书写自己的属性
  2. 必须要书写 super
  3. ES6 的类语法可以继承 ES5 的构造函数
   父类
    class Person {
      constructor (name) {
        this.name = name
      }

      sayHi () {
        console.log('Person')
      }
    }
    // 子类
    // 1. 在创建类的时候直接书写关键字进行继承
    class Student extends Person {
      constructor (age, name) {
        // 相当于我们的 借用继承
        // 2. 在 constructor 的最前面继承父类的属性
        super(name)
        this.age = age
      }

      run () {
        console.log('跑')
      }
    }

    const s1 = new Student(15, 'Jack')
    console.log(s1)

类继承构造函数

//父类
    function Person(name) {
      this.name = name
    }
    Person.prototype.sayHi = function () { console.log('Person') }

   // 子类继承自构造函数 Person
    class Student extends Person {
      constructor (age, name) {
        super(name)
        this.age = age
      }

      run () { console.log('跑') }
    }

    const s1 = new Student(16, 'Rose')
    console.log(s1)

总结

用了这么多方法来实现继承,无非是想使用父类的方法,复制还是原型等方法都不算最佳的解决,甚至一些方法称不上继承,在es6类中得到了很好的解决,:继承的好处:

  • a:提高了代码的复用性
  • b:提高了代码的维护性
  • c:让类与类之间产生了关系,是多态的前提
  • 但是在使用的时候,因为父类与子类之间存在强耦合关系,那么当修改父类时,子类会受到影响,当你仅仅为了一个或者二个子类来写继承也完全没有必要,如果有很多子类时,那么父类的属性和方法一旦修改就会导致子类产生影响,所以在写的时候要考虑周全。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MaxLoongLvs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值