面向对象与继承

image

类与实例

首先,通过两种方式来声明一个类:
第一种,构造函数的方式

1
2
3
function Animal1(name) {
this.name = name
}

第二种,ES6中的class声明

1
2
3
4
5
class Animal2 {
constructor(name) {
this.name = name
}
}

两种方式生成实例的方式是一样的

1
2
var animal = new Animal1('dog')
var animal2 = new Animal2('bear')

实现继承

第一种:借助构造函数实现继承

1
2
3
4
5
6
7
function Parent1() {
this.name = 'parent1'
}
function Child1() {
Parent1.call(this)
this.age = 14
}

这种继承的方式只能继承构造函数内部的属性,缺点是不能继承构造函数原型对象上的属性,如下

1
2
3
Parent1.prototype.say = function() {
console.log('hello')
}

这时候,say方法Child1是继承不到的

第二种:借助原型链实现继承

1
2
3
4
5
6
7
8
function Parent2() {
this.name = 'parent2'
}
function Child2() {
this.age = 16
}
Child2.prototype = new Parent2()
var child = new Child2()

这个方法中,child的属性方法先有构造函数Child2内部的属性age,然后我们知道每个对象都有一个_ proto _ 属性,指向构造函数的prototype,这时child._ proto _ === Child2.prototype,然后就继承到Parent2上的属性及方法了

这个方法也有个缺点,如果Parent2中有个属性值是引用类型的,如下

1
2
3
4
function Parent2() {
this.name = 'parent2'
this.color = ['red', 'green']
}

这时候我们生成两个child实例

1
2
3
var child1 = new Child2()
var child2 = new Child2()
child1.color.push('blue')

这时候child2.color也会有’blue’,这肯定不是我们想要的结果。这时因为child1._ proto _ 和child2._ proto _都指向Child2.prototype,而Child2.prototype的值是一个实例对象

第三种:组合方式

1
2
3
4
5
6
7
8
function Parent3() {
this.name = 'parent3'
}
function Child3() {
Parent3.call(this)
this.age = 16
}
Child3.prototype = new Parent3()

这种方式可以实现构造函数体内的属性以及原型对象上的属性与方法,但有个缺点,就是我们新生成一个child实例时,Parent3构造函数执行了两次,这样是没必要的,所以继续优化一下,如下:

1
2
3
4
5
6
7
8
function Parent4() {
this.name = 'parent4'
}
function Child4() {
Parent3.call(this)
this.age = 16
}
Child4.prototype = Parent4.prototype

这种方式也有一个缺点,就是新生成的child实例的constructor会指向Parent4而不是Child4,那继续来优化一下:

1
2
3
4
5
6
7
8
9
function Parent5() {
this.name = 'parent5'
}
function Child5() {
Parent3.call(this)
this.age = 16
}
Child5.prototype = Object.create(Parent5.prototype)
Child5.prototype.constructor = Child5

至此,继承就完美实现了。
有问题,欢迎指正!

--本文结束感谢您的阅读--