一文搞懂JavaScript原型继承

前言

昨天简单讲了原型的理解,今天我继续来讲一下原型的继承,以前面试遇到这种都是直接放弃的类型,希望今天过后,再遇到同样的问题,多多少少可以说出一点东西,那么今天这篇文章就算没有白写。

原型的继承方法

JavaScript中原型的继承方法有原型继承利用构造函数继承组合继承组合寄生式继承

原型继承

原型继承就是直接更改对象的prototype属性以达到继承的效果。

function Person() {
  this.name = 'lili'
}
Person.prototype.sayName = function () {
  console.log(this.name)
}
function Teacher(age) {
  this.age = age
}
Teacher.prototype = new Person()
Teacher.prototype.constructor = Teacher

const t1 = new Teacher(18)
console.log(t1.name, t1.age)  // lili 18
t1.sayName()  // lili
console.log(Teacher.prototype.constructor === Teacher)  // true

从打印结果我们可以知道,Teacher中并没有定义name属性及sayName方法,但是我们仍然可以在Teacher的实例中使用,这就是继承,可以理解为子类可以从父类中继承属性和方法,需要注意的是,我们使用了Teacher.prototype.constructor = Teacher修改了构造函数的指向,如果不加这一行,最后一行的打印结果会是false

利用构造函数实现继承

利用构造函数实现继承可以理解成在字类中直接调用父类,并指定执行父类的this指向。

function Person() {
  this.name = 'lili'
}
Person.prototype.sayName = function () {
  console.log(this.name)
}
function Teacher(age) {
  Person.apply(this) // 可以理解为这儿直接执行了一个函数,函数体只有一句话,this.name = 'lili',this指向和Teacher中相同
  this.age = age
}

const t1 = new Teacher(18)
console.log(t1.name, t1.age)  // lili 18
// t1.sayName()  // t1.sayName is not a function
console.log(Teacher.prototype.constructor === Teacher)

可以看到t1.sayName()直接报错,说明这种继承方法只能继承父类中显示定义的属性和方法,不能继承父类原型上面的方法。

组合继承

组合继承就是直接将原型集成和构造函数继承结和起来,我们直接修改上面的例子。

function Person() {
  this.name = 'lili'
}
Person.prototype.sayName = function () {
  console.log(this.name)
}
function Teacher(age) {
  Person.apply(this) // 可以理解为这儿直接执行了一个函数,函数体只有一句话,this.name = 'lili',this指向和Teacher中相同
  this.age = age
}
Teacher.prototype = new Person()
Teacher.prototype.constructor = Teacher // 修正构造函数
const t1 = new Teacher(18)
console.log(t1.name, t1.age)  // lili 18
t1.sayName()  // lili
console.log(Teacher.prototype.constructor === Teacher) // true

可以看出,这种继承方式可以同时继承父类显示定义的属性和方法及父类原型上的属性和方法。但是这种继承方法也有问题,就是构造函数Person执行了两次,一次是在Teacher.prototype = new Person()时,还有一次是在new Teacher()时,Teacher中使用Person.apply(this)调用了一次。这样字类实例和字类的原型中都有name属性,造成了资源浪费。

组合寄生式继承

组合寄生式继承式在组合继承的基础上改造而来,不直接调用Teacher.prototype = new Person()修改原型指向,而是利用一个中间对象来链接字类和父类。

function Person() {
  this.name = 'lili'
}
Person.prototype.sayName = function () {
  console.log(this.name)
}
function Teacher(age) {
  Person.apply(this) // 可以理解为这儿直接执行了一个函数,函数体只有一句话,this.name = 'lili',this指向和Teacher中相同
  this.age = age
}
function extend(childObj,parentObj) {
  const tempObj = Object.create(parentObj.prototype)
  tempObj.constructor = childObj
  childObj.prototype = tempObj
}
extend(Teacher,Person)
const t1 = new Teacher(18)
console.log(t1.name, t1.age)  // lili 18
t1.sayName()  // lili
console.log(Teacher.prototype.constructor === Teacher) // true

这样修改后,构造函数Person执行一次,属性name也只在子类的实例中有,子类原型上没有这个属性,代码变得更加纯粹。

在这里插入图片描述

往期文章

JavaScript原型初解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值