面向对象(二)继承

继承是面向对象的三大特性之一,在JS中实现继承就是通过原型与原型链来实现,

原型链

对象在查找属性或方法时,在自己身上找不到时,就会沿着当前对象的原型对象(隐式原型)进行查找,直到查找到最顶层的原型对象为null的对象,这个由原型串联起来的链就是原型链
例如创建了一个空对象obj,这时候如果使用.操作符获取属性age时,获取到的就是undefined

const obj = {}
console.log(obj.age) // undefined

因为不管是在obj本身还是原型链上都不能找到age

通过这种字面量形式创建的对象,实际上是一种语法糖,它等于new Object(),根据之前new关键字所做的操作可以验证(其他的js数据类型也一样)

console.log(obj.__proto__ === Object.prototype) // true

这时候如果通过__proto__obj的原型对象添加一个age属性,再次获取age属性时,就可以成功拿到

obj.__proto__.age = 111
console.log(obj.age) // 111

需要注意的是,如果是set操作,他会在自己身上添加该属性,而不会去查找原型链

const obj = {} 
// 给原型对象添加一个age
obj.__proto__.age = 111
// obj本身添加一个age
obj.age = 222
console.log(obj.age, obj.__proto__.age) // 222 111
// 可以看见它并没有修改原型对象上的age,而是给自己添加了一个age

接下来瞅瞅如何通过原型链来实现继承

实现继承

创建TeacherStudent两个对象

function Teacher(name, age, course) {
  this.name = name
  this.age = age
  this.course = course
}
Teacher.prototype.eating = function () {
  console.log("eating")
}
Teacher.prototype.say = function () {
  console.log("say")
}
Teacher.prototype.teaching = function () {
  console.log("teaching")
}
function Student(name, age, snum) {
  this.name = name
  this.age = age
  this.snum = snum
}
Student.prototype.eating = function () {
  console.log("eating")
}
Student.prototype.say = function () {
  console.log("say")
}
Student.prototype.study = function () {
  console.log("studying")
}

可以看见这两个对象有许多相同的属性与方法,那我们可以创建一个Person类,然后让上面两个对象继承于Person,接下来看看怎样来实现继承

继承(方法)
// Person 类
function Person() {}
Person.prototype.eating = function () {
  console.log("eating")
}
Person.prototype.say = function () {
  console.log("say")
}
错误方式

将父类的显示原型对象赋值给子类

Teacher.prototype = Person.prototype

这样做,子类确实拥有了父类的方法,但是现在这种情况,子类与父类的显式原型都指向了同一个对象,如图所示
在这里插入图片描述
所以当给子类添加方法时,该方法也会添加到父类上,这样这种方法就不可取

正确方式

我们知道当我们使用new关键字实例化对象的时候,会将构造函数的显式原型赋值给新对象的隐式原型,我们可以利用这点以及原型链的查找规则,可以实例化一个父类,然后将该对象赋值给子类的显式原型对象

Teacher.prototype = new Person()

在这里插入图片描述
这样子类在继承了父类方法的同时,也不会在为自己添加方法时而影响到父类了,但是目前也只是继承了方法而已,关于属性的继承还无法实现。

继承(属性)

属性的继承,通过借用构造函数来实现形如

function Teacher(name, age, course) {
  Person.call(this, name, age)
  this.course = course
}

在子类的构造函数中调用父类的构造函数并显式的将this绑定进去,这样就可以完成属性的继承了

组合式继承

我们将上面继承方法与属性的方式结合起来,就能够实现继承,这种方式叫组合式继承

function Person(name, age) {
  this.name = name
  this.age = age
}
Person.prototype.eating = function () {
  console.log("eating")
}
Person.prototype.say = function () {
  console.log("say")
}
function Teacher(name, age, course) {
   // 借用构造函数继承属性
  Person.call(this, name, age)
  this.course = course
}
// 继承方法
Teacher.prototype = new Person()

通过这种方式,就实现了继承,但是该方法还具有以下缺点

  1. 会调用两次父类构造函数
  2. 拥有了两份父类属性
    • 本身存在一份
    • 原型上有一份
      在这里插入图片描述
      为了解决上面的问题,所以有了寄生组合式继承
寄生组合式继承

上面继承父类方法的方式,其实目的就是创建一个新对象,而这个对象的原型指向父类的显式原型,所以还可以通过以下方法实现

function createObject(obj) {
  const newObj = {}
  Object.setPrototypeOf(newObj, obj)
  // 将 obj 作为 newObj 的隐式原型对象
  return newObj
}

我们调用该方法,并传入Person的原型对象

const obj = createObject(Person.prototype)
console.log(obj.__proto__ === Person.prototype) // true

可以看见现在的obj对象的原型就指向了Person的显式原型对象,这样就实现了方法的继承并且没有多余的属性,而属性的继承还是和刚才一样借用构造函数

function Teacher(name, age, course) {
  Person.call(this, name, age)
  this.course = course
}
const obj = createObject(Person.prototype)
Teacher.prototype = obj
const t = new Teacher("sakurige",22,"math")
console.log(t)

在这里插入图片描述
现在除了自己身上有一份属性以外,原型上是没有的了,这样大致继承效果就完成了,不过还有一个constructor属性需要创建一下,所以最后的继承应该是这样的

function Person(name, age) {
  this.name = name
  this.age = age
}
Person.prototype.eating = function () {
  console.log("eating")
}
Person.prototype.say = function () {
  console.log("say")
}
function Teacher(name, age, course) {
  Person.call(this, name, age)
  this.course = course
}
function createObject(obj) {
  const newObj = {}
  Object.setPrototypeOf(newObj, obj)
  return newObj
}
function inherit(SubType, SuperType) {
  SubType.prototype = createObject(SuperType.prototype) // 子类原型指向父类
  Object.defineProperty(SubType.prototype, "constructor", { // 定义 constructor 属性
    enumerable: false,
    configurable: true,
    writable: true,
    value: SubType,
  })
}
inherit(Teacher, Person)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值