【继承系列】JS中的构造函数继承

1.构造函数继承

在子类构造函数内部使用call或apply来调用父类构造函数

我们先来复习一下call和apply方法。

通过call()、apply()或者bind()方法直接指定this的绑定对象, 如foo.call(obj)使用.call()或者.apply()的函数是会直接执行的,而bind()是创建一个新的函数,需要手动调用才会执行

.call()和.apply()用法基本类似,不过call接收若干个参数,而apply接收的是一个数组
举个🌰:

const obj = {
  a:'abc'
};

function sayA(){ 
  console.log(this.a)
}
sayA.call(obj) // 'abc'
1.1 题目一

构造函数的基本原理

function Parent (name) {
  this.name = name
}
function Child () {
  this.sex = 'boy'
  Parent.call(this, 'child')
}
const child1 = new Child()
console.log(child1)

答案:

Child {sex: "boy", name: "child"}

理解:

  • 调用Parent函数在Child里面运行,而Parent函数的作用是为当前函数添加name属性,并且属性值是参数name

伪代码:

function Child(){
	this.sex = 'boy'
	// 伪代码
	this.name = 'child'
}
1.2 题目二

现在我在子类和父类中都加上name这个属性

function Parent (name) {
  this.name = name
}
function Child () {
  this.sex = 'boy'
  Parent.call(this, 'good boy')
  this.name = 'bad boy'
}
const child1 = new Child()
console.log(child1)

按照JS的执行顺序,所以答案为

Child {sex: "boy", name: "bad boy"}

当然,如果你把Parent.call(this, 'good boy')this.name = 'bad boy'换下位置,那肯定name就是good boy

function Parent (name) {
  this.name = name
}
function Child () {
  this.sex = 'boy'
  this.name = 'bad boy'
  Parent.call(this, 'good boy')
}
const child1 = new Child()
console.log(child1)

答案:

Child {sex: "boy", name: "good boy"}
1.3 题目三

构造函数继承的优点

解决了原型链继承中子类共享父类引用对象的问题

function Parent (name, sex) {
  this.name = name
  this.sex = sex
  this.colors = ['white', 'black']
}
function Child (name, sex) {
  Parent.call(this, name, sex)
}
const child1 = new Child('child1', 'boy')
child1.colors.push('yellow')

const child2 = new Child('child2', 'girl')
console.log(child1)
console.log(child2)

原型链继承中我们知道,子类构造函数创建的实例是会查找到原型链上的colors的,而且改动它会影响到其它的实例,这是原型链继承的一大缺点。

现在使用构造函数继承,我们看下结果

Child{ name: 'child1', sex: 'boy', colors: ['white', 'black', 'yellow'] }
Child{ name: 'child1', sex: 'boy', colors: ['white', 'black'] }

使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类,所以现在child1和child2现在分别有它们各自的colors了,就不共享了。

而且这种拷贝属于深拷贝,验证的方式是你可以把colors数组中的每一项改为一个对象,然后修改它看看。
举个🌰:

function Parent () {
  this.colors = [{ title: 'white' }, { title: 'black' }]
}

function Child(){
  Parent.call(this)
}

const child1 = new Child()
child1.colors[0].name = 'andy'

const child2 = new Child()

console.log(child1);
console.log(child2);

/*
  Child { colors: [ { title: 'white', name: 'andy' }, { title: 'black' } ]}
  Child { colors: [ { title: 'white' }, { title: 'black' } ] }
*/
1.4 题目四

构造函数继承只能继承父类的实例属性和方法,不能继承父类原型的属性和方法

function Parent (name) {
  this.name = name
}
Parent.prototype.getName = function () {
  console.log(this.name)
}
function Child () {
  this.sex = 'boy'
  Parent.call(this, 'good boy')
}
Child.prototype.getSex = function () {
  console.log(this.sex)
}
var child1 = new Child()
console.log(child1)
child1.getSex()
child1.getName()

答案:

Child {sex: "boy", name: "good boy"}
boy
`Uncaught TypeError: child1.getName is not a function`

理解:

  • sex、name属性都有这个我们都可以理解
  • getSex属于Child构造函数原型对象上的方法,我们可能是能用它的,这个也好理解
  • 而getName呢?它属于父类构造函数原型对象上的方法,使用Parent.call(this, ‘good boy’)只是让复制了一下Parent构造函数里的属性和方法,没让复制原型对象上的属性和方法。
1.5 题目五

实例并不是父类的实例,只是子类的实例

child1 instanceof Parent ==> false

这句话意思是:Parent.protorype不在实例child1的原型链上,因此也就有了题目1.4中实例不能使用构造函数原型对象上的属性和方法

function Parent (name) {
  this.name = name
}
function Child () {
  this.sex = 'boy'
  Parent.call(this, 'child')
}
var child1 = new Child()

console.log(child1) // Child {sex: "boy", name: "child"}
console.log(child1 instanceof Child) // true
console.log(child1 instanceof Parent) // false
console.log(child1 instanceof Object) // true

总结

构造函数继承:

  • 优点:

    • 解决了原型链继承中子类实例共享父类引用对象的问题
    • 创建子类实例时,可以向父类传递参数(见题目1.3)
  • 缺点:

    • 构造继承只能继承父类的实例属性和方法,不能继承父类原型的属性和方法(见题目1.4)
    • 实例并不是父类的实例,只是子类的实例(见题目1.5)

参考文章:

48道原型题目

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值