创: 前端二牛
借用构造函数继承解决了原型链数据共享和无法向超类型传递参数的问题,但自身的缺陷是不能使用超类型原型中定义的方法。组合继承是将原型链继承和借用构造函数继承组合到一起,从而发挥二者之长的一种继承模式,也被称作伪经典继承。背后的思路是使用原型链实现原型属性和方法的继承,使用构造函数来实现实例属性的继承。这样既通过在原型上定义方法实现了函数的复用,又能保证每个实例都有它自己的属性。
下面来看一个例子:
在这个例子中首先声明了一个 Human函数,然后使用原型模式定义了 sayName方法。声明了 Person函数,通过构造函数模式继承了 Human,但是这只能继承 Human中声明的属性,也就是实例属性,没办法继承原型属性和方法,于是紧接着将 Person的原型赋值为 Human的一个实例,通过原型链实现原型属性和方法的继承,然后添加了一个子类型特有的方法 sayName,这就是组合继承。测试使用,结果既可以通过构造函数向超类型传递参数,也没有 colors被共享的问题,同时还可以使用超类型原型上定义的方法 sayName,可以说非常完美。
组合继承避免了原型链和借用构造函数的缺陷,融合了它们的优点,成为JavaScript中最常用的继承模式。而且,因为使用了原型链, instanceOf和 isPrototypeOf也能够识别基于组合继承创建的对象。
为粉丝提出的建议 ,上一篇给大家讲了call()和apply()的基本使用方法 。这一篇文章中为大家讲解js继承 , 内容中也会结合call()方法的使用 , 算是趁热打铁 。 没有学过call()方法的要先学一下上篇文章哦 ~call()和apply()的用法
说到继承 ,童靴们最先想到的可能是原型继承了。如下:
如上方法中通过b.prototype=new a("hkk",23)实现b的原型继承了a , 但是这种方法有一个弊端 ,相信童靴们也能看出来 , 就是之后如果实例化函数b , 每个对象的属性name和age值都是一样的 ,都为“hkk”和23 。为了优化这个弊端 , 所以出现了组合继承。
组合继承结合call()方法和原型的方式实现继承 , 如下:
在函数b中使用call方法把父类a的属性继承 , 然后使用原型把父类原型上面的方法继承过来 。此时b也拥有了a上面的所有属性 ,所以就可以在实例化的时候为每个实例对象传入不同的参数 。当然,这种方法也有一个不足之处,就是使用call()方法已经拿到父类所有的属性 ,后面再使用原型时也会有父类所有属性 ,如下:
如上,尽管子类的属性会覆盖掉原型上面的属性 ,可毕竟重复的东西看不来不太好。为了改善这个不足 , 我们需要封装一个方法 ,如下:
封装如上函数 ,获取到传入函数的原型 , 在调用时传入父类函数 ,如下:
此时 ,子类b的原型就只会继承到父类a原型上面的方法 , 而不是重复的基础父类a的属性了。
最后还要注意的是 , 使用原型继承会使子类的构造器指向父类a ,所以我们需要用如下方法让b原型的构造器指向b.
写在这里 ,一个较完善的继承也就完成了。下面附上完整的例子:
你必须非常努力,才能看起来毫不费力 !
关注小白前端,持续收到文章推送~
组合继承是原性链继承和构造函数继承的合体,它汲取了二者各自的有点,同时又互相补充了各自的弱点,是一种应用十分广泛的JavaScript继承模式。下面分别从原性链继承、构造函数继承分别开始介绍,最后介绍二者的结合——组合继承。
一、原型链
利用原型让一个引用类型继承另一个引用类型的属性和方法
每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。
实现原性链的基本模式:
function SuperType(){ //定义了一个父函数 this.property=true; } SuperType.prototype.getSuperValue=function(){ //给父函数的原型链上添加一个getSuperValue的函数 returnthis.property; } function Subtype(){ //定义一个子函数 this.subproperty=false; } SubType.prototype=new SuperType(); //SubType实现了继承SuperType SubType.prototype.getSubValue=function(){ //给子函数添加方法 return this.subproperty; } var instance=new SubType(); // 实例instance继承子函数 alert(instance.getSuperValue());
最后的结果:intance指向SubType的原型,而SubType的原型又指向SuperType的原型,SuperType继承了Object,所有函数的默认原型都是Object的实例
问题:会产生引用类型值的问题
比如,创建了一个子类的实例,如果对子类实例的属性进行了修改,那么创建其他子类的时候都会收到影响,代码如下:
function SuperType(){ this.colors=[“red”, “blue”, “green”]; } function SubType(){ } SubType.prototype=new SuperType(); var instance1=new SubType(); instance1.colors.push(“black”); alert(instance1.colors); //red, blue, green, black var instance2=new SubType(); alert(instance2.colors); //red, blue, green, black
以上结果说明会影响其他实例的属性值
二、借用构造函数
在子类型构造函数的内部调用超类型构造函数
function SuperType(){ // 定义一个父函数 this.colors=[“red”, “blue”, “green”]; } function SubType{}( // 定义一个子函数 SuperType.call(this); // 继承了父函数 } var instance1=new SubType(); // 实例instance1继承子函数 instance1.colors.push(“black”); alert(intance1.colors); //red,blue,green,black var instance2=new SubType(); // 实例instance2继承子函数 alert(instance2.colors); //red,blue,green
使用该方法可以在子类构造函数中向超类型构造函数传递参数,如下:
function SuperType(name){ // 定义父函数 this.name=name; } function SubType(){ // 定义子函数 SuperType.call(this,“Nicholas”); //传入参数,利用这个参数初始化父类构造函数中的name this.age=29; } var instance=new SubType(); // 实例instance继承子函数 alert(instance.name); //Nicholas alert(instance.age); //29
问题:不方便复用
三、组合式继承
使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承
示例代码:
function SuperType(name){ // 定义父函数 this.name=name: // 定义子函数 this.colors=[“red”, “blue”,“green”]; } SuperType.prototype.sayName=function(){ //定义了一个方法,该方法在继承的子类中也可以用 alert(this.name); } function SubType(name, age){ SuperType.call(this, name); //继承SuperType的一部分,this指SubType, this.age=age; //自己新定义的属性age也可以进行赋值 } SubType.prototype=new SuperType(); //利用原型继承,可以使用父类的方法 SubType.prototype.sayAge=function(){ //定义SubType特有的新方法 alert(this.age); } var instance1=new SubType(“Martin”, 10); instance1.colors.push(“black”); alert(instance1.colors); //red,blue,green,black instance1.sayName(); //Martin instance1.sayAge(); //10 var instance2=new SubType(“Greg”, 27); alert(instance2.colors); //red,blue,green instance2.sayName(); //Greg instance2.sayAge(); //27
综合例子:
function Person(name, age) { this.name=name; this.age=age; } Person.prototype.hi=function() { console.log('Hi, my name is' + this.name) + ",I'm" + this.age + 'years old now.'; }; Person.prototype.LEGS_NUM=2; Person.prototype.ARMS_NUM=2; Person.prototype.walk=function() { console.log(this.name + "is walking..."); } function Student(name, age, className) { Person.call(this, name, age); this.className=className; } Student.prototype=Objectcreate(Person.prototype); Student.prototype.constructor=Student; Student.prototype.hi=function() { console.log('Hi, my name is' + this.name + ", I'm" + this.age + "years old now, and from" + this.className + "."); } Student.prototype.learn=function(subject) { console.log(this.name + 'is learing' + subject + 'at' + this.className + '.'); } // test var bosn=new Student('Bosn', 27, 'Class 3,Grade 2'); bosn.hi(); // Hi, my name is Bosn, I'm 27 years old now and from Class3,Grage 2 bosn.LEGS_NUM; // 2 bosn.walk(); // Bosn is walking bosn.learn('math'); // Bosn is learning math t Class3, Grade 2.
学习从来不是一个人的事情,要有个相互监督的伙伴,想要学习或交流前端问题的小伙伴可以私信“学习”小明获取web前端入门资料,一起学习,一起成长!
*请认真填写需求信息,我们会在24小时内与您取得联系。