1.寄生组合继承
我们先看下上次说到的组合继承缺点
- 父类构造函数会被调用两次
- 生成了两个实例(子类实例,和子类原型对象)
那现在我们需要跳过执行new Parent()
,并且直接继承父类原型链上属性
也就是说,我们需要一个干净
的实例对象,来作为子类的原型。并且这个干净的实例对象还得能继承父类的属性。
这就需要知道Object.create()
的用法了
Object.create(proto, propertiesObject)
参数:
- proto:新创建对象的原型对象。
- propertiesObject:可选。如果没有指定为 undefined,则是要添加到新创建对象的不可枚举(默认)属性(即其自身定义的属性,而不是其原型链上的枚举属性)对象的属性描述符以及相应的属性名称。这些属性对应Object.defineProperties()的第二个参数。
返回值:
一个新对象,带着指定的原型对象和属性。
例外:
如果propertiesObject
参数是null
或非原始包装对象,则抛出一个 TypeError 异常。
看一个🌰:
function A(){
this.name = 'aaa'
}
const o;
// 创建一个原型为A的空对象
o = Object.create(A.prototype);
o.__proto__ === A.prototype // true
明白他的使用了吧,当然你可能会有疑问为什么不用const o = {}
,因为这种方式创建的对象o.__proto__ --> Object.prototype
,自己可以尝试下,对create
详解请移步MDN。
1.1 题目一
理解寄生组合继承的用法
function Parent (name) {
this.name = name
}
Parent.prototype.getName = function () {
console.log(this.name)
}
function Child (name) {
this.sex = 'boy'
Parent.call(this, name)
}
// 组合继承
// Child.prototype = new Parent()
// 与组合继承的区别
Child.prototype = Object.create(Parent.prototype)
const child1 = new Child('child1')
console.log(child1)
child1.getName()
console.log(child1.__proto__)
答案:
Child {sex: "boy", name: "child1"}
child1
Parent {}
可以看到,上面👆这道题就是一个标准的寄生组合继承,它与组合继承的区别仅仅是Child.prototype
不同。
理解:
- 使用寄生组合继承,child1不仅仅有自己的实例属性sex,而且还复制了父类中的属性name
- 寄生组合继承使得实例child1能通过原型链查找,使用到Parent.prototype上的方法,因此打印出child1。
现在我们看看组合继承的缺点还存在不存在
function Parent (name) {
console.log(name) // 这里有个console.log()
this.name = name
}
function Child (name) {
Parent.call(this, name)
}
// 这里改成寄生组合继承
Child.prototype = Object.create(Parent.prototype)
const child1 = new Child('child1')
console.log(child1)
console.log(Child.prototype)
惊喜来了,缺点已被干掉
child1
Child {name: "child1"}
Parent {}
然后我们画下寄生组合继承的图
2.总结
寄生组合继承
- 优点
- 只调用了一次父类构造函数,只创建了一份父类属性
- 子类可以用到父类原型链上的属性和方法