JavaScript面向对象之组合-寄生式继承实现

2022 年 7 月 21 日 星期四
/
16

JavaScript面向对象之组合-寄生式继承实现

前言

组合-寄生式继承是javascript对象继承中比较好的方案,它是在一系列继承方案改进的结果

原型链继承

// 父类: 公共属性和方法
function Person() {
  this.name = "kun"
  this.friends = []
}

Person.prototype.eating = function() {
  console.log(this.name + " eating")
}

// 子类: 特有属性和方法
function Student() {
  this.sno = 111
}

//继承Student原型
Student.prototype = new Person()

//在student原型上,也就是Person构造函数new出的对象上添加studying方法
Student.prototype.studying = function() {
  console.log(this.name + " studying")
}

var stu = new Student()
obj.keys(stu) //无法遍历name
stu.name //kun
stu.studying() //kun studying
stu.eating() // kun eating

该方案实现了原型继承,但也有以下弊端

  1. stu对象无法遍历原型上的属性name
  2. 多个由Student创建的对象将共享name
  3. Student构造函数无法传递参数给name

借用构造函数继承(组合继承)

// 父类: 公共属性和方法
function Person(name, age, friends) {
  this.name = name
  this.age = age
  this.friends = friends
}


Person.prototype.eating = function() {
  console.log(this.name + " eating")
}

// 子类: 特有属性和方法
function Student(name, age, friends, sno) {
  //call将this传入Person构造函数中并调用,在Student构造函数存入父类属性
  Person.call(this, name, age, friends)
  //Student特有的属性
  this.sno = 111
}
//继承Student原型,但此时会将无用属性name\age\friends赋给Student原型
Student.prototype = new Person()

//在Student原型上,也就是Person构造函数new出的对象上添加studying方法
Student.prototype.studying = function() {
  console.log(this.name + " studying")
}

借用构造函数继承的方案解决了原型链继承中的诸多缺陷,比如借用构造函数继承中继承的属性可以遍历,创建多个子类 属性不共享,可以传递参数等。
该方案解决了原型继承的一些弊端,但也有以下缺陷

  1. 此时会将父类中无用属性(name\age\friends)赋给子类原型
  2. 父类至少要调用两次

寄生式继承

var personObj = {
  running: function() {
    console.log("running")
  }
}

//工厂函数
function createStudent(name) {
  //stu将其原型寄生在了personObj上 以此来实现继承
  var stu = Object.create(personObj)
  stu.name = name
  stu.studying = function() {
    console.log("studying~")
  }
  return stu
}

组合-寄生式继承

//Object.create()的 polyfill
function createObject(o) {
  function Fn() {}
  Fn.prototype = o
  return new Fn()
}
//原型继承方法
function inheritPrototype(SubType, SuperType) {
  //创建一个proto指向父类原型的对象赋给子类原型
  SubType.prototype = Object.create(SuperType.prototype)
  //将子类原型的原本指向父类的constructor指向自己
  Object.defineProperty(SubType.prototype, "constructor", {
    enumerable: false,
    configurable: true,
    writable: true,
    value: SubType
  })
}
//父类构造函数
function Person(name, age, friends) {
  this.name = name
  this.age = age
  this.friends = friends
}

Person.prototype.eating = function() {
  console.log(this.name + " eating")
}

//子类构造函数
function Student(name, age, friends, sno, score) {
  Person.call(this, name, age, friends)
  this.sno = sno
  this.score = score
}
//原型继承
inheritPrototype(Student, Person)

Student.prototype.studying = function() {
  console.log("studying")
}
var stu = new Student("kunkun", 18, ["kobe"], 111, 55)

stu.studying()
stu.eating()
console.log(stu.constructor.name)

该方案关键使用了Object.create()方法,传入父类原型后返回一个proto指向该原型的对象作为子类的原型,在创建子类对象时,执行Person.call(),将父类构造函数的属性也传入了子类。

使用社交账号登录

  • Loading...
  • Loading...
  • Loading...
  • Loading...
  • Loading...