JS的几种继承方式介绍
程序员文章站
2024-01-09 10:03:58
原型链继承
当我们每创建一个函数时,该函数都会自带一个prototype属性,该属性会指向另一个对象,指向的这个对象就是该函数的原型对象。原型对象中又有一个指向构造函数的属性。通过new 构造函数的...
原型链继承
当我们每创建一个函数时,该函数都会自带一个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
由代码结果可知,组合继承方式融合了原型链继承和构造方法继承的优点,又同时避开了它们的缺点。