面向对象的程序设计之继承

1.原型链

1) 例子,继承关系如图所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function SuperType () {
this.property = true
}
SuperType.prototype.getSuperValue = function () {
return this.property
}
function SubType () {
this.subProperty = true
}
SubType.prototype = new SuperType()
SubType.prototype.getSubValue = function () {
return this.subProperty
}
const instance = new SubType()
console.log(instance.getSuperValue())


原型链.jpg

2) 确定原型和实例的关系

  • 使用instanceof操作符:只要构造函数在原型中出现过,就会返回true
  • isPrototypeOf():只要是原型链上出现过的原型,都可以说是改原型链所派生的实例的原型

3) 原型链存在的问题

  • 在超类型的构造函数中定义的属性,会变成子类型的原型对象上的属性(原型对象上的属性会被所有实例共享)
  • 创建子类型时,不能向超类型的构造函数中传递参数

2.借用构造函数

1) 例子,继承关系如图所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function SuperType1 () {
this.color = ['red']
}
function SubType1 () {
this.subProperty = true
// 继承了SuperType
SuperType1.call(this)
}
const instance1 = new SubType1()
instance1.color.push('black')
const instance2 = new SubType1()
console.log(
instance1,
instance2
)


借用构造函数.jpg

2)借用构造函数实现继承的优势:可以像超类型的构造函数传递参数
3)存在的问题:无法共享方法

3.组合继承

1) 例子,继承关系如图所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function SuperType2 (name) {
this.name = name
this.color = ['red']
}
SuperType2.prototype.sayName = function () {}
function SubType2 (name, age) {
// 继承了SuperType
SuperType2.call(this, name)
this.age = age
}
SubType2.prototype = new SuperType2()
SubType2.prototype.sayAge = function () {}
const instance3 = new SubType2('CC', 12)
console.log(
instance3, // { name: 'CC', color: [ 'red' ], age: 12 }
SubType2.prototype // { name: undefined, color: [ 'red' ], sayAge: [Function] }
)


组合继承.jpg

2) 优点:避免了原型链和借用构造函数的缺陷
3) 缺点:会调用两次超类的构造函数,给子类型的原型对象添加了有多余的属性

4.原型式继承

1) 思路:基于已有的对象创建新对象

1
2
3
4
5
6
// 返回的新对象的[[prototype]]指向旧对象
function object (o) {
function F () {}
F.prototype = o
return new F()
}


原型式继承.jpg

2) ES5中的Object.create(obj, properties)方法实现了这种继承方法

1
2
3
4
5
6
7
const person = {name: 'XX'}
const anotherPerson1 = Object.create(person, {
name: {
value: 'BB'
}
})
console.log(anotherPerson1, anotherPerson1.name)

5.寄生式继承

1) 思路:创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后返回对象

1
2
3
4
5
6
7
function createAnother (original) {
var clone = Object.create(original)
clone.sayHi = function () { console.log('hi') }
return clone
}
const anotherPerson2 = createAnother(person)
console.log(anotherPerson2, anotherPerson2.name)


寄生式继承.jpg

6.寄生组合式继承

1) 解决的问题:解决组合式继承会调用两次超类构造函数的问题
2) 思路:通过借用构造函数来继承属性,通过寄生式继承来继承方法
3) 例子,继承逻辑如图所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// inheritPrototype的逻辑:重构了subType的原型对象,使其继承superType的原型对象
function inheritPrototype (subType, superType) {
const prototype = Object.create(superType) // 创建SuperType的副本
prototype.constructor = subType // 增强对象
subType.prototype = prototype // 指定对象
}
function SuperType (name) {
this.name = name
this.color = ['red']
}
SuperType.prototype.sayName = function () {}
function SubType (name, age) {
// 继承了SuperType的属性
SuperType3.call(this, name)
this.age = age
}
inheritPrototype(SubType3, SuperType3) // 继承了SuperType的方法
SubType3.prototype.sayAge = function () {} // 给SubType的原型对象添加方法
const instance4 = new SubType2('CC', 12)


寄生组合式继承.jpg