简介JS系列暂定27篇,从基础,到原型,到异步,到设计模式,到架构模式等,本文是JS系列的第三篇。文章重点介绍JS继承,包括原型链继承、构造函数继承、组合继承、寄生组合继承、原型继承、ES6继承、多重继承与new。ES5继承首先定义一个父类函数SuperType(){//属性this.name='SuperType';}//原型方法SuperType.prototype.sayName=function(){returnthis.name;};1.原型链继承基本思想是使用父类的实例作为子类的原型//parentclassfunctionSuperType(){this.name='SuperType';//父类属性}SuperType.prototype.sayName=function(){//父类原型方法returnthis.name;};//子类函数SubType(){this.subName="SubType";//子类属性};SubType.prototype=newSuperType();//重写原型对象,替换为新类型的实例//这里实例化一个SuperType时,实际上执行了两步//1、新创建的对象复制了父类构造函数中的所有属性和方法//2,以及原型__proto__指向父类的原型对象SubType.prototype.saySubName=function(){//子类原型方法returnthis.subName;}//子类实例letinstance=newSubType();//instanceof判断对象的原型//trueinstanceinstanceofSuperType;//true//注意SubTypeinstanceofSuperType;//falseSubType.prototypeinstanceofSuperType;//真正的特点:使用原型来做引用该类型继承了另一个引用类型的属性和方法优点:继承父类的模板,同时继承父类的原型对象缺点:可以添加实例属性子类构造函数中的子类实例。如果要添加新的原型属性和方法,则必须在语句SubType.prototype=newSuperType('SubType');之后执行。.无法实现多重继承来自原型对象的所有属性被所有实例共享//父类函数SuperType(){this.colors=["red","blue","green"];this.name="SuperType";}//子类函数SubType(){}//原型链继承SubType.prototype=newSuperType();//instance1varinstance1=newSubType();instance1.colors.push("blcak");instance1.name="change-super-type-name";console.log(instance1.colors);//["red","blue","green","blcak"]console.log(instance1.name);//更改-super-type-name//实例2varinstance2=newSubType();console.log(instance2.colors);//["red","blue","green","blcak"]console.log(instance2.name);//SuperType注意:当更改SuperType引用类型属性时,所有SubType实例将共享此更新。基类型属性更新不会。在创建子类实例时,不可能向父类构造函数传递参数,或者说,没有办法在不影响所有对象实例的情况下向超类构造函数传递参数。详细答案参考前端进阶面试题。继承的基本思想:在子类型构造函数内部调用父类型构造函数。注意:函数无非就是在特定环境下执行代码的对象,所以这里使用apply/call来实现。使用父类的构造函数来增强子类实例,相当于将父类的实例属性复制到子类中(没有使用原型)//parentclassfunctionSuperType(name){this.name=name;//父类属性}SuperType.prototype.sayName=function(){//父类原型方法returnthis.name;};//子类函数SubType(){//调用SuperType构造函数SuperType.call(this,'SuperType');//在子类构造函数中,给父类构造函数传递参数//为了保证子类构造函数不会改写子类的属性,需要在调用父类构造函数后定义子类的属性this.subName="子类型";//子类属性};//子类实例letinstance=newSubType();//运行子类构造函数,在子类构造函数中运行父类构造函数,this绑定Set给子类优点:解决1中子类实例共享父类引用对象的问题,实现多重继承,创建子类实例时,你可以给父类传递参数缺点:实例不是父类的实例,而是子类实例的实例只能继承父类的实例属性和方法,不能继承原型属性/方法。无法实现功能复用。每个子类都有一份父类的实例函数,影响性能。链式继承和构造函数继承相结合,充分发挥两者取长补短的继承模式。基本思想:使用原型链继承来使用原型属性和方法的继承,通过构造函数继承来实现实例属性的继承。这样既可以通过在原型上定义方法来实现功能复用,又可以保证每个实例都有自己的属性。通过调用父类构造,继承父类的属性并保留传递参数的优点,然后将父类实例作为子类原型实现函数重用//父类函数SuperType(name){this.colors=["红","蓝","绿"];this.name=名称;//父类属性}SuperType.prototype.sayName=function(){//父类原型方法returnthis.name;};//子类函数SubType(name,subName){//调用SuperType的构造函数SuperType.call(这个,名字);//----第二次调用SuperType----this.subName=subName;};//----第一次调用SuperType----SubType.prototype=newSuperType();//覆盖原型对象并用新的类型实例替换它SubType.prototype.constructor=SubType;//组合继承需要修复构造Function指向SubType.prototype.saySubName=function(){//子类原型方法returnthis.subName;}//子类实例letinstance=newSubType('An','sisterAn')instance.colors.push('black')console.log(instance.colors)//["red","blue","green","black"]instance.sayName()//Aninstance.saySubName()//sisterAnletinstance1=newSubType('An1','sisterAn1')console.log(instance1.colors)//["red","blue","green"]instance1.sayName()//An1instance1.saySubName()//sisterAn1第一次调用SuperType构造函数时,SubType.prototype会得到name和colors两个属性;调用SubType构造函数时,第二次调用SuperType构造函数,这次在新对象的属性上创建name和colors,这两个属性会屏蔽原型对象上的同名属性//instanceof:instance的原型链是对照SuperType.prototypeinstanceofSuperType//trueinstanceinstanceofSubType//true//isPrototypeOf:instance的原型链是对照SuperType本身SuperType.prototype.isPrototypeOf(instance)//trueSubType.prototype.isPrototypeOf(instance)//真正的优点:弥补了方法2的缺陷,可以继承实例属性/方法,也可以继承原型属性/方法,不存在共享引用属性的问题,可以传递参数,可复用缺点:父类构造函数被调用两次,生成两个实例(子类实例屏蔽了子类原型上的那个)4.寄生组合继承组合继承中,父类构造函数被调用两次,这里通过寄生的方式,切断父类的实例属性,这样当父类的构造被调用两次时,instance方法/属性不会被初始化两次,避免了组合继承的缺点。主要思想:借用构造函数继承属性,通过原型链的混合形式继承方法//父类函数SuperType(name){this.colors=["red","blue","green"];this.name=名称;//父类属性}SuperType.prototype.sayName=function(){//父类原型方法returnthis.name;};//子类函数SubType(name,subName){//调用SuperType构造函数SuperType.call(this,姓名);//----第二次调用SuperType,继承实例属性----this.subName=subName;};//----第一次调用SuperType,继承原型属性----SubType.prototype=Object.create(SuperType.prototype)SubType.prototype.constructor=子类型;//注意:增强对象letinstance=newSubType('An','sisterAn')优点:只调用一次SuperType构造函数,只创建父类属性原型链的副本,可以正常使用instanceof和isPrototypeOf5.原型继承实现思路:实现思路是将子类的原型设置为父类的原型//父类函数SuperType(name){this.colors=["red","blue","green"];this.name=名称;//父类属性}SuperType.prototype.sayName=function(){//父类原型方法returnthis.name;};/**第一步*///子类,继承父类的实例属性和方法通过调用类,不能继承原型属性/方法functionSubType(name,subName){SuperType.call(this,name);//调用SuperType的构造函数并传递参数this。subName=subName;}/**第二步*///解决调用不能继承父类原型属性/方法的问题//Object.create方法接受一个对象作为新创建对象的原型,并创建具有指定Prototype的对象和具有多个指定属性的对象//该方法指定的任何属性将覆盖原型对象上的同名属性SubType.prototype=Object.create(SuperType.prototype,{constructor:{//注意指定SubType.prototype.constructor=SubTypevalue:SubType,enumerable:false,writable:true,configurable:true},run:{value:function(){//overrideSuperType.prototype.run.apply(这个,参数);//调用super//...},enumerable:true,configurable:true,writable:true}})/**步骤3*///最后:解决SubType.prototype.constructor===SuperType问题//这里,在上一步已经指定了,这里不用操作//SubType.prototype.constructor=SubType;varinstance=newSubType('An','sistenAn')muchInheritance如果想继承更多,可以使用mix-in方法//parentclassSuperTypefunctionSuperType(){}//parentclassOtherSuperTypefunctionOtherSuperType(){}//多重继承子类函数AnotherType(){SuperType.call(this)//继承SuperType的实例属性和方法OtherSuperType.call(this)//继承OtherSuperType的实例属性和方法}//继承一个类AnotherType.prototype=Object.create(SuperType.prototype);//使用Object.assign混合其他Object.assign(AnotherType.prototype,OtherSuperType.prototype);//Object.assign会将OtherSuperType原型上的函数复制到AnotherTypeprototype,这样AnotherType的所有实例都可以使用OtherSuperType的方法//ReassignconstructorAnotherType.prototype.constructor=AnotherType;AnotherType.prototype.myMethod=function(){//做一个thing};letinstance=newAnotherType()最重要的部分是:SuperType.call继承实例属性方法使用Object.create()继承原型属性和方法修改指向ES6继承的SubType.prototype.constructor首先实现一个简单的ES6inheritance:classPeople{constructor(name){this.name=name}run(){}}//extends相当于方法继承//替换上面3行代码classManextendsPeople{constructor(name){//super相当于属性的继承//替换People.call(this,name)super(name)this.gender='male'}fight(){}}核心代码extends继承的核心代码如下,其实现与上面的寄生组合继承方法相同function_inherits(subType,superType){//创建对象,Object.create创建父类原型的副本//增强对象以弥补默认构造函数属性丢失由于重写了原型//Specify对象,将新创建的对象赋值给子类的原型真实的,可配置的:真实的}});if(superType){Object.setPrototypeOf?Object.setPrototypeOf(subType,superType):subType.__proto__=superType;}}继承的使用场景不要仅仅为了使用而使用它们,那只是浪费时间当你需要创建一系列具有相似属性的对象时,创建一个包含所有通用功能的泛型对象,然后在更多中继承那些属性特定的对象类型。应该避免多重继承,造成混乱。注:考虑到JavaScript的工作方式,由于原型链等特性的存在,不同对象之间的功能共享通常称为委托——特殊对象将功能委托给一般对象类型。这可能比称之为继承更合适,因为“继承”的功能并没有被复制到“继承”的对象中,而是仍然存在于普通对象中。
