1. 原型链继承
将子类的原型对象指向父类的实例
1.1 题目一
function Parent () {
this.name = 'Parent'
this.sex = 'boy'
}
Parent.prototype.getName = function () {
console.log(this.name)
}
function Child () {
this.name = 'child'
}
// 将Child的原型对象指向Parent的实例对象
Child.prototype = new Parent()
const child1 = new Child()
child1.getName()
console.log(child1)
答案
'child'
Child {name: "child"}
理解:
- child1是Child的实例对象,有属性name
- 子类构造函数Child它的原型被指向了父类构造函数Parent创建出来的"无名实例"
- 这样的话,我child1就可以使用你这个"无名实例"里的所有属性和方法,因此child1.getName()有效。
console.log(this.name)
这里因为child1中有属性name,就不再沿原型链寻找name,可以尝试把Child中name去掉,结果就是Parent - 另外由于sex、getName都是Child原型对象上的属性,所以并不会表现在child1上。
这就是原型链继承,我们看张图,我是喜欢用三角来画原型三角恋的
所以现在你知道了吧,这种方式就叫做原型链继承。
将子类的原型对象指向父类的实例。
1.2 题目二
注意原型链继承不是 A原型 = B原型
function Parent () {
this.name = 'Parent'
this.sex = 'boy'
}
Parent.prototype.getSex = function () {
console.log(this.sex)
}
function Child () {
this.name = 'child'
}
Child.prototype = Parent.prototype
const child1 = new Child()
child1.getSex()
console.log(child1)
这道题是错误的,只想说明上面这种方式是错误的
1.3 题目三
理解原型链继承的优点和缺点
function Parent (name) {
this.name = name
this.sex = 'boy'
this.colors = ['white', 'black']
}
function Child () {
this.feature = ['cute']
}
const parent = new Parent('parent')
Child.prototype = parent
const child1 = new Child('child1')
child1.sex = 'girl'
// child1.colors = ['test']
child1.colors.push('yellow')
child1.feature.push('sunshine')
const child2 = new Child('child2')
child2.feature.push('rain')
console.log(child1.name)
console.log(child1.colors)
console.log(child1.feature)
console.log(child2.colors)
console.log(child2.feature)
console.log(parent)
答案:
parent
VM278:24 (3) ["white", "black", "yellow"]
VM278:25 (2) ["cute", "sunshine"]
VM278:27 (3) ["white", "black", "yellow"]
VM278:28 (2) ["cute", "rain"]
VM278:29 Parent {name: "parent", sex: "boy", colors: Array(3)}
-
在child1实例中添加了一个新属性
sex
-
child1是没有
colors
属性的,需要去原型链上去寻找,找到colors
并pushyellow
,此时修改的是parent上的属性,会影响到后续所有的实例对象 -
(这里可能会有疑问,为什么child1.colors不是在child1上新增,而sex就是,说下我的理解)
- 设置colors时候,是先取到colors,发现parent上有colors,再进行push方法,如果你直接
child1.colors = ['test']
,那么child1中就会有你创建的这个colors2,那么push的yellow也就在colors2中 - 设置sex时候,不用先取到sex,可直接赋值,于是就在child1实例中直接创建sex属性
- 设置colors时候,是先取到colors,发现parent上有colors,再进行push方法,如果你直接
-
child2.colors由于用的也是原型对象parent上的colors,又由于之前被child1给改变了,所以打印出来的会是
['white', 'black', 'yellow']
-
feature它是属于实例自身的属性,它添加还是减少都不会影响到其他实例。
-
child1.name是原型对象parent上的name,也就是’parent’,虽然我们在new Child的时候传递了’child1’,但它显然是无效的,因为接收name属性的是构造函数Parent,而不是Child。
2.总结
现在我们就可以得出原型链继承它的优点和缺点了
优点:
-
继承了父类的模板,又继承了父类的原型对象
缺点: -
来自父类构造函数的所有属性都被共享了,这样如果不小心修改了父类构造函数中的引用类型属性,那么所有子类创建的实例对象都会受到影响(这点从修改child1.colors可以看出来)
-
如果要给子类的原型上新增属性和方法,就必须放在Child.prototype = new Parent()这样的语句后面,如果放在前面当然找不到原型呀,因为这句话连接了父类与子类(具体看图)
-
创建子类时,无法向父类构造函数传参数(这点从child1.name可以看出来)