首页 > 代码库 > js中的继承
js中的继承
面向对象语言中都支持两种继承:接口继承和实现继承,但是由于js中函数没有签名(接收的参数类型和数量不同),所以无法实现接口继承。
下面讲讲js中实现继承的方法:
一、原型继承
原型继承:既继承了父类的模板,又继承了父类的原型对象。
function Person(name,age){ this.name=name; this.age=age;}Person.prototype.sayName=function(){ alert(this.name);}function Boy(){}Boy.prototype=new Person(‘a‘,12);Boy.prototype.sayHello=function(){ alert(‘hello‘);}var boy=new Boy();alert(boy.name);//aboy.sayName();//aalert(boy.constructor);//function Person(...){...}alert(boy instanceof Person);//truealert(boy instanceof Boy);//true
由上可见,子类实例的构造函数指向了父类的构造函数(Boy.prototype指向了另一个原型,而这个原型的constructor是父类)。子类实例又是Person类又是Boy类。但是,有一个问题:若构造函数中需传参,就必须要在new Person()上传参,若在创建子类实例中传参则会显示undefined,如下:
function Person(name,age){ this.name=name; this.age=age;}Person.prototype.sayName=function(){ alert(this.name);}function Boy(){}Boy.prototype=new Person();Boy.prototype.sayHello=function(){ alert(‘hello‘);}var boy=new Boy(‘a‘,12);alert(boy.name);//undefined
虽然它既继承了父类的模板又继承了父类的原型对象,但若是在子类中传递参数,则会被屏蔽。
原型继承的图解:
构造函数.prototype=原型
原型.constructor=构造函数
实例中的内部指针指向原型
function Person(){}Person.prototype.setName=function(){};var person=new Person();function Boy(){}Boy.prototype=person;var boy=new Boy();
继承调用方法的搜索步骤:①搜索实例 ②搜索子类.prototype ③ 搜索父类.prototype
二、借用构造函数的方式继承
这种继承只继承模板,而不继承原型对象,这就会导致函数无法复用。
function Person(name,age){ this.name=name; this.age=age;}function Boy(name,age){ Person.call(this,‘a‘);//继承了父类的模板 } var boy=new Boy(); alert(boy.name);//a
三、组合继承(最常用的继承模式)
组合继承是将原型继承和借用构造函数结合起来。
function Person(name,age){ this.name=name; this.age=age;}Person.prototype.sayName=function(){ alert(this.name);}function Boy(name,age,sex){ this.sex=sex; Person.call(this,name,age);}Boy.prototype=new Person();Boy.prototype.sayHello=function(){alert(‘hello‘);}var boy=new Boy(‘a‘,12,‘男‘);alert(boy.name);//aalert(boy.sex);//男boy.sayName();//aboy.sayHello();//hello
但是这种继承由于Person.call()继承了一次父类的模板,又因Boy.prototype=new Person(),继承了一次原型对象,并且又继承了一次模板。这就导致继承了两次父类的模板。那我们能不能直接将父类原型赋给子类原型呢?
function Person(name,age){ this.name=name; this.age=age;}Person.prototype.sayName=function(){ alert(this.name);}function Boy(name,age,sex){ this.sex=sex;}Boy.prototype.sayHello=function(){alert(‘hello‘);}Boy.prototype=new Person();var boy=new Boy(‘a‘,12,‘男‘);alert(boy.name);//aalert(boy.sex);//男alert(boy.constructor);boy.sayName();//aboy.sayHello();//报错
不行,因为若直接将父类原型赋给子类原型,则当调用子类原型中定义的方法将会报错:Uncaught TypeError: boy.sayHello is not a function。那么我们要如何解决这个问题呢?
我们可以自己定义一个只继承父类原型对象的方法:
function extend(sub,sup){ function F(){};//创建一个空的构造函数 F.prototype=sup.prototype;//将父类的原型赋给空构造函数的原型 sub.prototype=new F(); sub.prototype.constructor=sub;//还原子类的构造函数 sub.superClass=sup.prototype;//保存父类的原型对象 if(sub.prototype.constructor==Object.prototype.constructor){ sub.prototype.constructor=sub;//防止子类的构造函数是object } }function Person(name,age){ this.name=name; this.age=age;}Person.prototype.sayName=function(){ alert(this.name);}function Boy(name,age,sex){ this.sex=sex; Boy.superClass.constructor.call(this,name,age);//之前保存的原型对象在这里就起到了解除耦合的作用了}extend(Boy,Person);Boy.prototype.sayHello=function(){alert(‘hello‘);}var boy=new Boy(‘a‘,12,‘男‘);alert(boy.name);//aalert(boy.sex);//男boy.sayName();//aboy.sayHello();//hello
这样就会只继承一次父类的模板和一次原型对象。上面的判断构造函数是为了如果对下面的原型对象上设置了一个以对象字面量形式创建的新对象,那么此原型对象的constructor属性会变成新的constructor属性,不再指向原来的构造函数,而是指向了Object。
上面的这种方法就是所谓的寄生组合式继承。他结合了原型式继承和寄生式继承,是引用类型最理想的继承方式。
那么什么是原型式继承,什么是寄生式继承?
四、原型式继承
在一个函数内创建一个空构造函数,将传入的对象作为空构造函数的原型,然后返回空构造函数的实例。这种继承必须要有一个对象作为另一个对象的基础。
function object(obj){ function F(){}; F.prototype=obj; return new F();}var person={ name:‘a‘, age:12};var person2=object(person);alert(person.name);//aalert(person2.name);//a
这种继承适用于只想让一个对象与另一个对象保持类似。
在ECMA5中新增了Object.create(用作新对象原型的对象,为新对象定义额外属性的对象)方法,此方法和我们定义的object()的作用差不多。
支持的浏览器:IE 9+ 、Firefox 4+、Safari 5+、Opera 12+、Chrome。
五、寄生式继承
在内部以某种方式增强对象,再返回此对象。
function object(obj){ function F(){}; F.prototype=obj; return new F();}function createAnother(sup){ var o=object(sup); o.sayHello=function(){ alert(‘hello‘); }; return o; }var person={ name:‘a‘, age:12 };var person2=createAnother(person);person2.sayHello();//hello
这种继承方式适用于对象不是自定义类型和构造函数。
js中的继承