JavaScript继承

JavaScript继承

原型链继承

每个构造函数都有一个原型对象prototype,原型通过constructor属性指回构造函数。实例通过原型链prptp指向实例原型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/** 
* 子类SubType通过创建父类SuperType的实例并将其赋值给自己的原型,prototype实现对父类SuperType的继承
* 重写子类SubType最初的原型,替换成父类SuperType的实例
* 创建子类SubType的实例并调用父类SuperType的方法
*
*
* 关系
* instance实例通过内部的[[Prototype]]指向SubType Prototype
* SubType Prototype 通过SuperType的实例又通过内部的[[Prototype]]指向SuperType.prototype
* getSuperValue方法在SuperType.prototype上
*
*/
function SuperType () {
this.property = 'SuperType'
}
SuperType.prototype.getSuperValue = function () {
return this.property
}
function SubType () {
this.subproperty = 'SubType'
}

// 继承SuperType
SubType.prototype = new SuperType()
SubType.prototype.getSubValue = function () {
return this.subproperty
}

let instance = new SubType()
console.log(instance.getSuperValue())


/**
* 问题
* 原型中包含引用值会在所有实例间共享
* 子类型在实例化时不能给父类型的构造函数传参
*/
function SuperType2 () {
this.colors = ['red', 'blue']
}

function SubType2 () {}

SubType2.prototype = new SuperType2()

let instance2 = new SubType2()
instance2.colors.push('black') // ["red", "blue", "black"]
console.log(instance2.colors)

let instance3 = new SubType2()
console.log(instance3.colors) // ["red", "blue", "black"]

盗用构造函数继承

在子类构造函数中调用父类构造函数。函数就是在特定上下文中执行代码的简单对象,可以使用apply()和call()方法以新创建的对象为上下文执行构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 /** 
* SuperType构造函数在SubType的实例创建的新对象的上下文中执行了
* 新的SubType对象运行了SuperType()函数中所有的初始化代码
*
* 优点
* 在子类的构造函数中向父类构造函数传参
*
* 缺点
* 必须在构造函数定义方法,函数不能重用。
* 子类也不能访问父类原型上定义的方法
*/
function SuperType (name) {
console.log(name)
this.colors = ['red', 'blue']
}

function SubType () {
SuperType.call(this, 'Nicholas')
}

let instancel = new SubType()
console.log(instancel.colors)

组合继承

使用原型链继承原型上的属性和方法,通过盗用构造函数继承实例属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
 /** 
* 优点
* 弥补了原型链和盗用构造函数的不足,instanceof操作符和isPrototypeOf()识别合成对象的能力
*
* 缺点
* 父类构造函数始终会被调用两次,一次是创建子类原型时调用,一次是在子类构造函数中调用
*/

function SuperType (name) {
this.name = name
this.colors = ['red', 'blue']
}

SuperType.prototype.sayName = function () {
console.log(this.name)
}

function SubType (name, age) {
// 继承属性
SuperType.call(this, name) // 第二次调用SuperType()
this.age = age
}

// 继承方法
SubType.prototype = new SuperType()

SubType.prototype.sayAge = function () {
console.log(this.age)
}

let instancel = new SubType('Nicholas', 29) // 第一次调用SuperType()
instancel.colors.push('black')
console.log(instancel.colors)
instancel.sayName()
instancel.sayAge()

let instancel2 = new SubType('Greg', 29)
console.log(instancel.colors)
instancel.sayName()
instancel.sayAge()

原型式继承

创建一个临时构造函数,将传入的对象赋值给这个构造函数的原型,然后返回这个临时类型的一个实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function object (o) {
function F () {}
F.prototype = o
return new F()
}

let person = {
name: 'Nicholas',
friends: ['Shelby', 'Count', 'Van']
}

// person对象定义了另一个对象也应该共享的信息,把它传给object()返回一个新对象,这个新对象就是person
// person.friends会anotherPerson和yetAnotherPerson共享
let anotherPerson = object(person)
anotherPerson.name = 'Greg'
anotherPerson.friends.push('Rob')

let yetAnotherPerson = object(person)
yetAnotherPerson.name = 'Linda'
yetAnotherPerson.friends.push('Barbie')

console.log(person.friends)
console.log(person.name)

寄生式继承

创建一个实现继承的函数,以某种方式增强对象,然后返回这个对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function object (o) {
function F () {}
F.prototype = o
return new F()
}

function createAnother (original) {
// 调用函数创建一个新对象
let clone = object(original)
// 以某种方式增强对象
clone.sayHi = function () {
console.log('hi')
}
// 返回这个对象
return clone
}

let person = {
name: 'Nicholas',
friends: ['Shelby', 'Count', 'Van']
}

// 新返回的anotherPerson对象具有person的所有属性和方法
let anotherPerson = createAnother(person)
anotherPerson.sayHi()

寄生式组合继承

不通过调用父类构造函数给子类原型赋值,而是取得父类原型的一个副本,使用寄生式继承来继承父类原型,然后返回的新对象赋值给子类原型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 只调用了一次父类构造函数,而且原型链仍然保持不变
function object (o) {
function F () {}
F.prototype = o
return new F()
}

function inheritPrototype (subType, superType) {
// 创建对象,创建父类原型的一个副本
let prototype = object(superType.prototype)
// 返回的prototype对象设置constructor属性,解决由于重写原型导致默认constructor丢失的问题
prototype.constructor = subType
// 将新创建的对象赋值给子类型的原型
subType.prototype = prototype
}

function SuperType (name) {
this.name = name
this.colors = ['red', 'blue']
}

SuperType.prototype.sayName = function () {
console.log(this.name)
}

function SubType (name, age) {
SuperType.call(this, name)
this.age = age
}

inheritPrototype(SubType, SuperType)

SuperType.prototype.sayAge = function () {
console.log(this.age)
}

let instancel = new SubType()
console.log(instancel.colors)