ES5中的继承

一、 原型链继承

原型链继承的原理:直接让子类的原型对象指向父类实例,当子类实例找不到对应的属性和方法时,就会往它的原型对象,也就是父类实例上找,从而实现对父类的属性和方法的继承。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 父类
function fat () {
this.name = 'aqing'
}
// 父类上的原型方法
fat.prototype.getName = function() {
return this.name
}
// 子类
function child () {}
// 子类的原型对象方法指向父类 子类中找不到的属性方法就会向父类找
child.prototype = new fat()
// 不影响继承 顺便绑一下constructor
child.prototype.constructor = child
// child实例就可以访问父类及其原型上的属性和方法了
const Child = new child()
Child.name // 'aqing'
Child.getName() // 'aqing'

原型链继承的缺点

  1. 所有的child实例原型都指向同一个fat实例,因此对child实例修改某个父类引用类型会影响所有的child实例;

  2. 创建子类时无法向父类传参,没有实现super()的功能.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function fat () {
this.name = 'aqing'
}

fat.prototype.getName = function() {
return this.name
}

function child () {}
child.prototype = new fat()
child.prototype.constructor = child
//
const Child1 = new child();
const Child2 = new child();
Child1.name = 'wzg'
console.log(Child1.name) // wzg
// 本来父类name属性是zaq 然后现在==
console.log(Child2.name) // wzg

二、 构造函数继承

构造函数继承:在子类的构造函数中执行父类的构造函数,并为其绑定子类的this。让父类的构造函数把成员的属性和方法都挂在子类的this上,这样既避免了共用一个原型实例,又能像父类构造函数传参。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function fat(name) {
this.name = name
}
fat.prototype.getName = function() {
return this.name
}

function Child(name) {
fat.call(this, name)
}
const child1 = new Child("aqing")
const child2 = new Child("aqing")
child1.name = 'wzg'
console.log(child1.name) // wzg
console.log(child2.name) // aqing

构造函数继承的缺点
继承不到父类上的属性和方法

1
child1.getName() // Uncaught TypeError: child1.getName is not a function

三 、组合式继承
既然原型链继承和构造函数继承都有各自的缺点但是又能互补,那何不将两者结合起来使用;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function fat(name) {
this.name = name;
}
fat.prototype.getName = function() {
return this.name;
}
// 构造函数继承
function Child(name) {
fat.call(this, name)
}
// 原型链继承
Child.prototype = new fat();
Child.prototype.constructor = Child;

const child1 = new Child("aqing");
const child2 = new Child("aq");
child1.name = 'wzg';
console.log(child1.name); // wzg
console.log(child2.name); // aq
console.log(child1.getName()); //wzg

组合式继承的缺点
每次创建子类实例都执行了两次构造函数(fat.call()和new fat()),虽然不影响继承,但是在子类创建时 原型中会存在两份相同的属性和方法。

四 、寄生式组合继承

为了解决构造函数被执行两次的问题,我们将指向父类实例 变成转向父类原型,减少一次构造函数的执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function fat(name) {
this.name = name;
}
fat.prototype.getName = function() {
return this.name;
}
// 构造函数继承
function Child(name) {
fat.call(this, name);
}
// 原型链继承
Child.prototype = fat.prototype // 将指向父类实例转成转向父类原型
Child.prototype.constructor = Child;

const child1 = new Child("aqing");
const child2 = new Child("aqing");
child1.name = 'wzg';
console.log(child1.name); // wzg
console.log(child2.name); // aqing
console.log(child1.getName()); // wzg

但是这种方法也存在一个问题,就是子类和父类的原型都指向同一个对象,如果我们对子类原型操作就会对父类原型产生影响。比如给子类Child.prototype新增一个getName方法,那么父类fat.prototype也增加或是被覆盖一个getName方法。为了解决这个问题 我们给fat.prototype做一个浅拷贝;

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
function fat(name) {
this.name = name;
}
fat.prototype.getName = function() {
return this.name;
}
// 构造函数继承
function Child(name) {
fat.call(this, name);
}
// 原型链继承
Child.prototype = Object.create(fat.prototype) // 将指向父类实例转成转向父类原型
Child.prototype.constructor = Child;

const child1 = new Child("aqing");
const child2 = new Child("aqing");
child1.name = 'wzg';
console.log(child1.name); // wzg
console.log(child2.name); // aqing
console.log(child1.getName()); // wzg
Child.prototype.sayname = function() {
console.log("sayname");
}
var f = new fat();
child1.sayname(); //saynem
f.sayname(); //Uncaught TypeError: f.sayname is not a function

ES6中的继承

class介绍来自阮一峰
es6中的继承:
1、class可以理解为functon,由于class本质还是一个function,因此它也有一个prototype属性。当new一个class时,会把class的protortype属性赋值给这个新对象的_proto_属性上;
2、constructor是默认添加的方法,在new一个对象的时候,会自动调用该方法, constructor里定义自己的属性;
3、继承extends和super, class子类名 extends父类名 实现继承。当然,还得在constructor里写上super (父类的参数),意思就是在子类中获取父类的this指针,相当于fat.call(this)

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
// 父类
class fat {
constructor(props) {
this.name = props || '';
}
getName () {
return this.name
}
}
// 继承
class Child extends fat {
constructor(props, attrs) { // props继承父类的属性,attrs自己私有的属性
super(props); // 相当于获取父类的this指向
this.rename = props.rename || ''
this.attrs = attrs
}
// 子类自己的方法
getFatname () {
return this.name
}
getAttrs () {
return this.attrs
}
}
// 通过new实例化一个构造函数
const child1 = new Child({
name: 'wzg',
rename: 'aqing'
}, 'wuzhiguang')
child1.getName() // wzg
child1.getRename() // aqing
child1.getAttrs() // wuzhiguang


愿你的坚持终有收获。