原型链继承
当我们每创建一个函数时,该函数都会自带一个prototype属性,该属性会指向另一个对象,指向的这个对象就是该函数的原型对象。原型对象中又有一个指向构造函数的属性。通过new 构造函数的实例会指向原型对象。通过该特性,可以通过原型链实现继承。
1 //定义父类
2 function Parent(){
3 this.property='超类';
4 }
5 Parent.prototype.constructor=Parent;
6 Parent.prototype.getValue=function(){
7 return this.property;
8 }
9 //定义子类
10 function Son(){
11 this.property=false;
12 }
13 //通过原型继承Parent
14 Son.prototype=new Parent();
15 Son.prototype.constructor=Son;
16 //在子类重写父类方法或添加新方法时,重写的方法和新添加方法的代码需要放在继承语句之后。
17 //重写父类的方法
18 Son.prototype.getValue=function(){
19 console.log('重写的getValue方法');
20 }
21 //新添加的方法
22 Son.prototype.showMsg=function(){
23 console.log('新添加的方法');
24 }
25 var s=new Son();
26 s.getValue(); //重写的getValue方法
27 s.showMsg(); //新添加的方法
需要注意的一点是,在通过原型链继承时,不能使用对象字面量的方式给原型对象方法,因为这样会重写掉原型链(对象字面量都是Object的实例)。
1 function Parent(){
2 this.attr='test attr';
3 }
4 function Son(){}
5 //原型链继承Parent
6 Son.prototype=new Parent();
7 //通过对象字面量给原型添加方法
8 Son.prototype={
8 showAttr=function(){
10 console.log(this.attr);
11 }
12 };
13
14 var s=new Son();
15 s.showAttr(); //报错
原型链的问题
当通过原型链进行继承时,原来父类型的引用类型的属性会变成子类型原型对象的实例。即该属性会被子类型实例的公共属性。
1 function Super(){
2 this.colors=['blue','red'];
3 }
4
5 function Sub(){}
6 Sub.prototype=new Super();
7 var sub1=new Sub();
8 sub1.colors.push('green');
9 console.log(sub1.colors); //["blue", "red", "green"]
10 var sub2=new Sub();
11 console.log(sub2.colors); //["blue", "red", "green"]
构造函数继承
构造函数继承可以解决原型链继承的问题。
在子类型构造函数中使用call和apply方法调用父类型的构造方法实现继承
1 function Super(name,age){
2 this.friends=['kkk','abc'];
3 this.name=name;
4 this.age=age;
5 }
6
7 function Sub(){
8 //Super.call(this);
9 Super.apply(this,arguments);
10 }
11 var s=new Sub('aaa',22);
12 s.friends.push('new');
13 console.log(s.friends); //["kkk", "abc", "new"]
14 console.log(s.name); //aaa
15 console.log(s.age); //22
16
17 var s1=new Sub();
18 console.log(s1.friends); // ["kkk", "abc"]
由s.friends和s1.friends返回的结果可知,原型链继承的问题可以由构造方法继承方法解决,但这种继承方法也存在问题:
构造函数继承的问题
由于所有的属性和方法都在构造函数上,每创建一个实例,相同功能的方法却同时占不同的内存,造成内存浪费。
组合继承
组合继承、即由原型链继承和构造函数继承组合在一起实现继承。该继承方式简单来说就是使用原型链继承原型的方法、通过构造函数继承方式继承属性。
1 //组合继承
2 function Super(name,age){
3 this.name=name;
4 this.age=age;
5 this.friends=['aaa','bbb'];
6 }
7 Super.prototype={
8 constructor:Super,
9 getMsg:function(){
10 console.log(this.name+' '+this.age)
11 }
12 }
13
14 function Sub(name,age,hobby){
15 //属性通过构造方法继承
16 //Super.call(this,name,age);
17 Super.apply(this,[name,age]);
18
19 //补充:为了避免父类的属性重写子类新增的属性或重写子类的属性,应将继承语句放在子类的第一行。
20 this.hobby=hobby;
21 }
22 //通过原型链继承方法。
23 Sub.prototype=new Super();
24 Sub.prototype.constructor=Sub;
25 Sub.prototype.getSubMsg=function(){
26 console.log(this.name+' '+this.age+' '+this.hobby+' '+this.friends);
27 }
28 var s=new Sub('kevin',22,'篮球');
29 s.friends.push('ccc');
30 s.getMsg(); //kevin 22
31 s.getSubMsg(); //kevin 22 篮球 aaa,bbb,ccc
32
33 var s1=new Sub('paul',28,'电影');
34 s1.getMsg(); //paul 28
35 s1.getSubMsg(); //paul 28 电影 aaa,bbb
由代码结果可知,组合继承方式融合了原型链继承和构造方法继承的优点,又同时避开了它们的缺点。