JavaScript中的继承(最全版)


一、为什么要继承?

1.为了把公共的内容提取出来变成更加公共的内容
2.为了让所有的类都能使用

二、继承的含义?

  1. 当 A 构造函数的实例, 能够使用 B 构造函数的 属性(构造函数体内)和方法(构造函数原型)
  2. 我们说 A 构造函数(类) 继承自 构造函数B(类)
    + 我们管 A 构造函数(类) 叫做 子类
    + 我们管 B 构造函数(类) 叫做 父类

三、原型继承?

原型继承
+ 核心: 子类的原型指向父类的实例
          => 核心: 子类的原型指向父类的实例
+ 通过改变原型链来实现继承
          => 当你访问一个对象的成员的时候
          => 会按照原型链规则查找
+ 优点
          => 属性和方法 都能继承下来
+ 缺点
          => 子类不能存在自己的私有方法, 一旦你想 子类.prototype 上插入方法, 就是在向父类的实例身上插入
          => 继承下来的属性是一模一样的

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

    // 子类
    function Student(gender) {
      this.gender = gender
    }

    // 实现继承
    Student.prototype = new Person('Rose', 18)

    const s = new Student('男')
    console.log(s)
    console.log(s.gender)
    console.log(s.name)
    s.sayHi()

    const s2 = new Student('女')
    console.log(s2)

四、借用继承

         借用继承 / call 继承(需要利用 call 或者 apply 方法)
+ 核心: 利用 call 来调用父类构造函数, 改变构造函数内的 this 指向
          => 父类.call(子类的this)
+ 构造函数也是函数, 通过 call 方法改变 this 指向
          => 实现讲父类内添加的内容插入到子类内
+ 优点
          => 子类可以有自己的原型
          => 每一个继承下来的属性都是自己的独立私有属性
+ 缺点
          => 父类的方法不能继承

// 分析父类:
    // 如果你想用 Person 创建实例
    // new 关键字做了几个事情
    //   创建了一个对象
    //   this 指向这个对象
    //   this.__proto__ 指向构造函数的原型
    //   返回这个对象
    // const p = new Person('Jack', 18)
    // 如果没有 new 关键字
    // 构造函数的本质, 依旧是函数, 可以不和 new 关键字连用
    // 不会自动创建对象了, 不会设置 __proto__ 了, 不会返回这个对象
    // 问题: 代码要不要执行 ?
    // 单纯的考虑一个函数的调用, this 指向谁了 ?
    // 因为 this 指向 window 了
    // 所以两个成员添加到 window 身上了
    // const p2 = Person('Rose', 20)
    // console.log(p2)
    // 只要是函数, 就可以通过 call 和 apply 和 bind 方法改变 this 指向
    // Person 函数也利用 call 方法改变一下 this
    // const obj = {}
    // 让 Person 内的 this 指向 obj
    // 当 Person 函数执行的时候, 两个成员添加到了 obj 身上
    // Person.call(obj, 'Jerry', 22)
    // console.log(obj)
    // 父类
    function Person(name, age) {
      this.name = name
      this.age = age
    }
    Person.prototype.sayHi = function () { console.log('hello world') }

    // 子类
    function Student(gender, name, age) {

      // 在这里把 Person 当做普通函数执行一遍
      // 并且在调用的时候, 使用 call 来调用, 改变 this 指向
      // 在这个函数内, this 指向 Student 的实例
      // 我现在利用 call 把 Person 函数内的 this 指向了 Student 的实例
      // Person 函数内添加的两个成员, 就加在了 Student 的实例 身上
      Person.call(this, name, age)

      this.gender = gender
    }

    // 子类自己的原型方法
    Student.prototype.study = function () { console.log('学习中...') }


    const s = new Student('男', 'Jack', 18)
    const s2 = new Student('女', 'Rose', 20)


    console.log(s)
    console.log(s2)

五、组合继承1 (原型继承 和 借用继承)

核心: 把 原型继承 和 借用继承 放在一起使用
1.优点:
         => 属性和方法 都能继承下来
          => 每一个继承下来的属性都是自己的独立私有属性
2. 缺点:
          => 没有自己的原型
          => 多一套属性, 虽然没有值, 但是多出来了

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

    // 子类
    function Student(gender, name, age) {
      // 利用借用继承继承属性
      Person.call(this, name, age)

      this.gender = gender
    }

    // 利用原型继承继承方法(属性和方法都能继承)
    Student.prototype = new Person()

    const s = new Student('男', 'Jack', 18)
    console.log(s)
    s.sayHi()

六、拷贝继承

1.in 关键字的作用
          => 语法: ‘key’ in 对象
          => 得到结果: 一个布尔值
          -> true, 表示该 key 在该对象的原型链上(可能是自己的属性, 也可能是原型上的属性)
          -> false, 表示该 key 在该对象的整个原型链上查找不到

  1. hasOwnProperty()
              => 语法: 对象.hasOwnProperty(‘key’)
             => 返回值: 一个布尔值
              -> true, 表示该 key 是该对象自己本身的属性
             -> false, 表示该 key 不是该对象自己本身的属性

  2. 拷贝继承 / for in 继承
             + 核心: 利用 in 关键字可以遍历到自己和原型上的成员
              + 把父类实例的所有内容, 遍历一份到子类的原型上

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

    // 子类
    function Student(gender) {
      this.gender = gender
    }
    Student.prototype.study = function () { console.log('学习中...') }

    // 准备一个子类的实例
    const p = new Person('Jack', 18)
    // 遍历父类的实例
    for (let k in p) {
      Student.prototype[k] = p[k]
    }

    const s = new Student('男')
    console.log(s)

七、组合继承2 (借用继承 和 拷贝继承)

结合了 借用继承 和 拷贝继承的一部分 实现的继承方案
1.出发点
         => 没有自己的私有属性, 利用 借用继承
         => for in 的遍历和属性不可改, 在书写构造函数的时候, 原型上写的是方法, 构造函数体内写的是属性
         -> 如果我的 for in 不去继承属性
         -> 之继承原型上的方法

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

    // 子类
    function Student(gender, name, age) {
      // 利用借用继承继承属性
      Person.call(this, name, age)

      this.gender = gender
    }
    Student.prototype.study = function () { console.log('学习中...') }

    // 子类的原型继承父类的原型
    // 利用 for in 循环只从 父类.prototype 开始遍历
    for (let k in Person.prototype) {
      Student.prototype[k] = Person.prototype[k]
    }

    const s = new Student('男', 'Jack', 18)
    console.log(s)

八、寄生继承

    // 寄生式组合继承(完善)
    // 父类
    function Person(name, age) {
      this.name = name
      this.age = age
    }
    Person.prototype.sayHi = function () { console.log('hello world') }
    // 子类
    function Student(gender, name, age) {
      // 利用借用继承继承属性
      Person.call(this, name, age)
      this.gender = gender
    }
    // -----------------------------------------------
    ; (function () {
      // 把 寄生继承 和 借用继承 完美结合
      function Third() { }
      // 2. 利用赋值操作, 让 Third 的 原型 等于 父类的原型
      Third.prototype = Person.prototype
      // 3. 利用原型继承, 让 Student 继承 Third
      Student.prototype = new Third()
      // 因为没有constructor所以自己补了一个
      Student.prototype.constructor = Student
    })()
    // -----------------------------------------------
    Student.prototype.study = function () { console.log('学习中...') }


    const s = new Student('男', 'Jack', 18)
    console.log(s)

九、ES6 的类继承

1.需要用到 extends 关键字
         => 语法: class 子类 extends 父类 {}
2.需要用到 super 关键字(超类)
          => 需要在子类的 构造器(constructor) 内调用使用
          => 注意: 需要调用在 this.xxx 的前面

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

    // 子类
    // 创建一个继承自 Person 的 Student 类
    class Student extends Person {
      constructor (gender, name, age) {
        // super 等价于原先的 借用继承
        super(name, age)

        this.gender = gender
      }

      study () { console.log('学习中...') }
    }


    const s = new Student('男', 'Jack', 18)
    console.log(s)
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Woli美美啊

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

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

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

打赏作者

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

抵扣说明:

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

余额充值