探讨 JS 的面向对象中继承的那些事
最近学了 JS 的面向对象,这篇文章主要是探讨 JS 的面向对象中继承的那些事。
JS中继承的特点:
1、子类继承父类;
2、子类可以用父类的方法和属性
3、子类的改变可以不影响父类
下面用一个例子来说明 JS 的继承
这段代码创建了一个父类以及它的原型,同时还创建了一个子类,并继承了父类的私有属性
1 <script> 2 //这是父类 3 function Father(name,age,marry){ 4 this.name=name; 5 this.age=age; 6 this.marry=marry; 7 } 8 //父类的原型 9 Father.prototype.showName=function(){ 10 alert(this.name); 11 } 12 13 //子类 14 function Son(name,age,marry,weight){ 15 Father.call(this,name,age,marry); 16 this.weight=weight; 17 } 18 19 </script>
当子类Son想继承父类的原型时,我的做法一开始是这么做的
1 <script> 2 //这是父类 3 function Father(name,age,marry){ 4 this.name=name; 5 this.age=age; 6 this.marry=marry; 7 } 8 //父类的原型 9 Father.prototype.showName=function(){ 10 alert(this.name); 11 } 12 13 //子类 14 function Son(name,age,marry,weight){ 15 Father.call(this,name,age,marry); 16 this.weight=weight; 17 } 18 19 //错误的做法 20 Son.prototype=Father.prototype; 21 Son.prototype.showAge=function(){ 22 alert(this.age); 23 } 24 var father=new Father('王大锤',30,true); 25 alert(father.showAge); 26 27 </script>
运行的结果可以发现,子类原型的改变影响了父类的原型,父类的原型中本来是没有showAge方法的,这就违背了前面继承的第三个特点了。
分析原因:上面代码的第20行 Son.prototype=Father.prototype;这里的 '=' 两边都是对象,那么它代表的意思就是引用,如果是引用的话,左边的对象改变,肯定会影响了右边的对象
所以才出现了子类原型的改变影响了父类的原型。
解决办法
方法一:核心思路,前面的问题不是说 '=' 是引用的关系才导致问题的嘛,那这里就保证 '=' 永远是赋值的关系,而不是引用。这里就定义一个 Clone() 方法,将父类对象拷贝给子类。
Clone() 方法里用到递归的原因是,在拷贝的过程中对象中可能嵌套对象。
1 <script> 2 //这是父类 3 function Father(name,age,marry){ 4 this.name=name; 5 this.age=age; 6 this.marry=marry; 7 } 8 //父类的原型 9 Father.prototype.showName=function(){ 10 alert(this.name); 11 } 12 13 //子类 14 function Son(name,age,marry,weight){ 15 Father.call(this,name,age,marry); 16 this.weight=weight; 17 } 18 Son.prototype=new Clone(Father.prototype); 19 Son.prototype.showAge=function(){ 20 alert(this.age); 21 } 22 var father=new Father('王大锤',30,true); 23 alert(father.showAge); 24 25 //通过克隆对象:核心思路是保证 '=' 是赋值的关系,而不是引用,也就是保证 '=' 的右边不是对象 26 function Clone(obj){ 27 for(var i=0;i<obj.length;i++){ 28 if(typeof(obj[key]=='object')){ 29 this.key=new Clone(obj[key]); 30 }else{ 31 this.key=obj[key]; 32 } 33 } 34 } 35 </script>
这时候的结果父类对象的showAge方法是undefined
方法二:代码很简单,但是很难想到,没有第一个方法那么好理解。核心思想:对象自身属性的改变,不会影响其构造函数的属性的改变。
1 <script> 2 //这是父类 3 function Father(name,age,marry){ 4 this.name=name; 5 this.age=age; 6 this.marry=marry; 7 } 8 //父类的原型 9 Father.prototype.showName=function(){ 10 alert(this.name); 11 } 12 13 //子类 14 function Son(name,age,marry,weight){ 15 Father.call(this,name,age,marry); 16 this.weight=weight; 17 } 18 function fn(){} 19 fn.prototype=Father.prototype; 20 Son.prototype=new fn(); 21 Son.prototype.showAge=function(){ 22 alert(this.age); 23 } 24 var father=new Father('王大锤',30,true); 25 alert(father.showAge); 26 27 //通过克隆对象:核心思路是保证 '=' 是赋值的关系,而不是引用,也就是保证 '=' 的右边不是对象 28 // Son.prototype=new Clone(Father.prototype); 29 // function Clone(obj){ 30 // for(var i=0;i<obj.length;i++){ 31 // if(typeof(obj[key]=='object')){ 32 // this.key=new Clone(obj[key]); 33 // }else{ 34 // this.key=obj[key]; 35 // } 36 // } 37 // } 38 </script>