Toc
  1. 介绍
  2. 基于原型链实现继承
  3. 寄生组合式继承
  4. 封装一下继承方法
  5. 问题
Toc
0 results found
Utone
模拟实现原型链继承
2021/02/15 Code JavaScript

介绍

只有函数对象才有属性prototype

每个实例对象都有一个私有属性__proto__,指向它的构造函数的原型对象prototype

每个实例对象共享原型对象上属性、方法。也就是实例对象从原型对象上继承了属性、方法

基于原型链实现继承

当访问对象中的方法、属性时,会先搜寻对象本身,若存在则停止搜寻。若不存在,则会去搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜寻,如果找不到,则返回undefined

按照上述原理,就可以将父类的属性、方法,设置为子类的原型,那么子类就相当于继承了父类的属性、方法,并且子类可以重写属性、方法(可以称为”属性遮蔽”)

寄生组合式继承

function Parent(name) {
  this.name = name
}

Parent.prototype.sayName = function () {
  console.log(this.name)
}

function Child(name) {
  // 仅使用call,只继承了父类构造函数的属性、方法
  // 父类构造函数的原型对象中的属性、方法,都没有继承过来
  Parent.call(this, name)
}

// 所以子类的原型还需要继承父类的原型
// 使用Object.create生成一个新对象,这样子类的原型与父类的原型就不会共享
// 在子类原型上修改属性、方法,不会影响到父类
Child.prototype = Object.create(Parent.prototype)

// 使子类的constructor指向正确
Child.prototype.constructor = Child

let ins = new Child('Child')

console.log(ins.name) // 'Child'
ins.sayName() // 'Child'

封装一下继承方法

// 子类需要在函数体中执行,Parent.call(this)
// 这样才能继承父类构造函数中的属性、方法
function inherit(child, parent) {
  parent.call(child)
  child.prototype = Object.create(parent.prototype)
  child.prototype.constructor = child
  return child
}

问题

为什么属性定义在构造函数上,方法需要定义在构造函数的原型上?

查看了阮一峰的Javascript 继承机制的设计思想一文,有以下感悟:

  1. 属性、方法定义在哪里并非强制。定义在原型上,纯粹是为了共享,对于所有实例都会共享的属性、方法,就可以定义在原型上
  2. 一般在实际开发中,属性一般不需要共享,一般都是子类在 new 时,去复写父类属性
  3. 方法一般是繁琐庞大,且可以复用,那每次实例就复制父类的所有方法,就会造成内存方面的问题

为什么要使用Object.create(Parent.prototype),来新生成一个对象,达到寄生的作用?

因为直接让父类的原型赋值给子类的原型(Child.prototype = Parent.prototype),这样就导致原型共享。想要在子类的原型上添加属性、方法,就会影响到父类,所以使用寄生作用,避免影响父类

// Object.create的模拟实现
function Create(o) {
  function F() {}
  F.prototype = 0
  return new F()
}
打赏
支付宝
微信
PWA
本文作者:Utone
版权声明:本文首发于Utone的博客,转载请注明出处!