原生js:对象继承

78 阅读2分钟

一、原型链继承

子类的原型是父类的实例,子类可以继承的所有私有属性和方法,并且可以继承父类的原型上的属性和方法

      function Teacher() {}
      Teacher.prototype = {
        constructor: Teacher,
        name: '老师',
        tSkill: 'js'
      }
      function Student(name) {
        this.name = name
        this.mSkill = 'html'
      }
      Student.prototype = new Teacher()
      const s = new Student('小明')
      console.log(s)

image.png

缺点:来自父类的所有属性和方法(包括原型)都会被继承,有时我们并不需要这么多属性

二、利用call/apply调用父类构造函数,借用父类的属性和方法

      function Teacher(name) {
        this.name = name
      }
      Teacher.prototype = {
        tSkill: 'js'
      }
      function Student(name, age) {
        Teacher.call(this, name) // 利用call/apply调用父类构造函数
        this.age = age
      }
      const s = new Student('小明', 18)
      console.log(s)

image.png

缺点:只能借用父类构造函数中的属性,无法借用父类原型上的属性

这种方式是借用,不是继承,因为子类的实例不属于父类

      function Person() {}
      function Teacher() {
        Person.apply(this)
      }
      // Teacher.prototype = Person.prototype // 打开注释的这行,便是继承
      const t = new Teacher()
      console.log(t instanceof Person) // false

三、圣杯模式

最标准的面向对象实现模式:基于原型的构造函数模式

若直接写Student.prototype = Teacher.prototype,则在更改Student原型上属性时会同步更改到Teacher原型。通过圣杯模式则可以避免这种情况

圣杯模式:声明一个构造函数,它的原型继承于父类,它的实例赋值给子类的原型,这样子类的原型中就继承了父类的原型,在操作子类的原型时不会操作到父类的原型

      function Teacher() {
        this.name = '老师'
        this.tSkill = 'js'
      }
      Teacher.prototype = {
        bSkill: 'html'
      }
      const t = new Teacher()
      function Student() {
        this.name = '学生'
      }
      function Buffer() {
        this.bufferName = '我是中间件'
      }
      Buffer.prototype = Teacher.prototype // Buffer的实例的原型将会有Teacher原型上的属性
      const b = new Buffer()
      Student.prototype = b // Student的实例的原型和Buffer的实例b保持一致
      Student.prototype.age = 18 // 更改子类原型上的属性,b的实例会同步更改,但实例不会影响到原型,所以不会更改到Buffer和Teacher
      const s = new Student()
      console.log(t)
      console.log(b)
      console.log(s)

image.png

封装inherit函数:

      function Teacher() {
        this.name = '老师'
      }
      Teacher.prototype = {
        tSkill: 'js'
      }
      // function Buffer() {}
      // Buffer.prototype = Teacher.prototype
      // const b = new Buffer()
      function Student() {
        this.name = '学生'
      }
      // Student.prototype = b
      inherit(Student, Teacher)
      Student.prototype.age = 10
      const t = new Teacher()
      const s = new Student()

      console.log(t)
      console.log(s, s.tSkill)

      function inherit(Target, Origin) {
        function Buffer() {}
        Buffer.prototype = Origin.prototype
        Target.prototype = new Buffer()
        Target.prototype.constructor = Target // 还原构造器
        Target.prototype.super_class = Origin // 设置继承源
      }

inherit函数还可以通过闭包的形式定义

      const inherit = (function () {
        const Buffer = function () {}
        return function (Target, Origin) {
          Buffer.prototype = Origin.prototype
          Target.prototype = new Buffer()
          Target.prototype.constructor = Target
          Target.prototype.spuer_class = Origin
        }
      })()

image.png