JS实现继承的几种方式
一、借用构造函数
1 function Father(val){ 2 this.val=val; 3 this.arr=[1]; 4 } 5 function Child(val){ 6 Father.call(this,val); 7 } 8 var child1=new Child(); 9 console.log(child1.val);
缺点:子类无法继承父类的原型对象,并没有真正的实现继承(部分继承)
如果给上述代码再添加几行:
1 function Father(val){ 2 this.val=val; 3 this.arr=[1]; 4 this.fun=function(){ 5 //.... 6 } 7 } 8 function Child(val){ 9 Father.call(this,val); 10 } 11 var child1=new Child(1); 12 var child2=new Child(2); 13 child1.arr.push(2); 14 15 console.log(child1.val); //1 16 console.log(child2.val); //2 17 18 console.log(child1.arr); //1,2 19 console.log(child2.arr); //1 20 21 console.log(child1.fun === child2.fun); // false
上面代码中21行报错,是因为无法继承方法,每个子类实例都有一个新的方法,无法实现函数复用
二、原型链
1 function Father(){ //父类 2 this.name='张三'; 3 this.arr=[1]; 4 } 5 function Child(){ //子类 6 this.type="hello" 7 } 8 Child.prototype=new Father(); //子类的原型等于父类的实例 9 var child1=new Child(); //实例化一个子类对象 10 console.log(child1.name);
缺点:原型对象的属性是共享的
如果给上述代码再添加几行:
1 function Father(){ //父类 2 this.name='张三'; 3 this.arr=[1]; 4 } 5 function Child(){ //子类 6 this.type="hello" 7 } 8 Child.prototype=new Father(); //子类的原型等于父类的实例 9 10 var child1=new Child(); //实例化一个子类对象 11 var child2=new Child(); //实例化一个子类对象 12 child1.name='李四' 13 child1.arr.push(2); 14 15 console.log(child1.name); //李四 16 console.log(child2.name); //张三 17 18 console.log(child1.arr); //1,2 19 console.log(child2.arr); //1,2
此时就出现了一个问题,第13行代码明明是给child1实例添加了属性,为什么第19行代码显示child2也拥有了该属性呢?这就是原型链继承所存在的问题,原型对象的属性是共享的。
三、组合式继承
原型继承+构造函数继承
1 function Father(){ //父类 2 this.val=1; //只在此处声明父类属性 3 this.arr=[1]; 4 } 5 Father.prototype.sayHai=function(){ //只在此处声明父类方法 6 //... 7 } 8 function Child(){ 9 Father.call(this); //构造函数继承 10 } 11 Child.prototype=new Father(); //原型链继承 12 13 var child1=new Child(); 14 var child2=new Child(); 15 16 console.log(child1.sayHai==child2.sayHai); //true
将实例方法放到原型对象里,以实现函数复用,同时还要保留借用构造函数方式的优点
优点:不存在属性共享问题,函数可复用
缺点:父类构造函数被调用了两次,代码冗余
四、寄生组合继承