本周面试题一览:原型链继承的基本思路是什么?优缺点都有什么?借用构造函数和组合继承的基本思想是什么?优缺点都有什么?原型继承的基本思想是什么?优缺点都有什么?寄生继承的基本思想是什么?优缺点都有什么?寄生组合继承的基本思想是什么?优缺点都有什么?本周的主题是继承。在开始之前,你需要了解构造函数、原型和原型链的相关知识。更多优质文章可戳:https://github.com/YvetteLau/...构造函数构造函数与普通函数的区别仅在于调用方式。任何函数,只要通过new运算符调用,就可以作为构造函数;任何一个函数,如果不是通过new操作符调用的,那么它就是一个普通函数。实例有一个构造函数(constructor)属性,它返回创建实例对象的构造函数。functionPerson(name,age){this.name=name;this.age=age;}varYvette=newPerson('刘晓曦',20);console.log(Yvette.constructor===Person);//true需要注意的一点是,除了基本数据类型的构造函数(null和undefined没有constructor属性),constructor属性是可以改写的。因此,instanceof运算符在检测对象类型方面比contsrutor更可靠。functionPerson(name){this.name=name;}functionSuperType(){}varYvette=newPerson('刘晓曦');console.log(Yvette.constructor);//[功能:人]伊薇特。构造函数=超类型;console.log(Yvette.constructor);//[Function:SuperType]prototype我们创建的每个函数都有一个prototype属性,它指向函数的原型对象。原型对象的目的是包含可以由特定类型的所有实例共享的属性和方法。默认情况下,所有原型对象都会自动获得一个构造函数属性,该属性包含指向原型属性所在函数的指针。当调用构造函数创建新实例时,该实例将包含一个指向构造函数原型对象的指针(可以通过实例的__proto__访问构造函数的原型对象)。functionPerson(name){this.name=name;}Person.prototype.sayName=function(){console.log(this.name);}varperson1=newPerson('刘晓曦');varperson2=newPerson('前台小姐姐');//构造函数原型对象上的方法和属性被实例person1.sayName();person1.sayName();共享instance.__proto__===constructor.prototype原型链简述构造函数、原型和实例的关系:每个构造函数都有一个原型对象,原型对象包含一个指向构造函数的指针,实例包含一个内部指针,可以执行原型对象(可通过__proto访问)。如果我们让原型对象等于另一个类型的实例,那么原型对象包含一个指向另一个原型的指针,相应地,另一个原型也包含一个指向另一个构造函数的指针。添加另一个原型就是另一个类型的实例,那么上面的关系仍然成立,这样递进的方式就形成了实例链和原型链,这就是原型链的基本概念。functionSuperType(){this.type='animal';}SuperType.prototype.getType=function(){console.log(this.type);}functionSubType(){}SubType.prototype=newSuperType();SubType.prototype.sayHello=function(){console.log('hello');}functionSimType(name){this.name=name;}SimType.prototype=newSubType();SimType.prototype.sayHi=function(){console.log('hi');}varinstance=newSimType('刘晓曦');实例.getType();一图胜千言:调用instance.getType()会调用如下查找步骤:搜索过程会一直向前,直到原型链的末端,然后停下来。所有的引用类型都继承了Object,而这种继承也是通过原型链来实现的。如果你没有在SuperType.prototype中找到getType,你会在Object.prototype中找到它(图中缺少一个链接)。25.原型链继承原型链继承的基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。如SubType.prototype=newSuperType();functionSuperType(){this.name='Yvette';this.colors=['pink','blue','green'];}SuperType.prototype.getName=function(){returnthis.name;}functionSubType(){this.age=22;}SubType.prototype=newSuperType();SubType.prototype.getAge=function(){returnthis.age;}SubType.prototype.constructor=SubType;letinstance1=newSubType();instance1.colors.push('黄色');控制台.log(instance1.getName());//'Yvette'console.log(instance1.colors);//['pink','blue','green','yellow']letinstance2=newSubType();console.log(instance2.colors);//['pink','blue','green','yellow'']可以看出colors属性会被所有实例(instance1,instance2,...)共享。缺点:通过原型实现继承时,原型会变成另一个类型的实例,原来的实例属性变成当前原型属性,原型的引用类型属性会被所有实例共享。创建子类型的实例时,无法在不影响所有对象实例的情况下将参数传递给超类型的构造函数。26.借用构造函数借用构造函数的基本思想是在子类型构造函数中调用超类型构造函数。functionSuperType(name){this.name=name;this.colors=['pink','blue','green'];}functionSubType(name){SuperType.call(this,name);}letinstance1=newSubType('Yvette');instance1.colors.push('yellow');console.log(instance1.colors);//['pink','blue','green',yellow]letinstance2=newSubType('Jack');console.log(instance2.颜色);//['pink','blue','green']优点:可以给超类传递参数,解决原型中包含的引用类型值被所有实例共享问题缺点:方法已定义在构造函数中,函数重用是不可能的,超类型原型中定义的方法对子类型不可见。27.组合继承组合继承是指将原型链和借用构造函数技术结合在一起,充分发挥两者优势的一种继承模式。基本思想:利用原型链实现原型属性和方法的继承,通过借用构造函数实现实例属性的继承。这样既通过在原型上定义方法实现了功能重用,又保证了每个实例都有自己的属性。functionSuperType(name){this.name=name;this.colors=['pink','blue','green'];}SuperType.prototype.sayName=function(){console.log(this.name);}functionSuberType(name,age){SuperType.call(this,name);this.age=age;}SuberType.prototype=newSuperType();SuberType.prototype.constructor=SuberType;SuberType.prototype.sayAge=function(){console.log(this.age);}letinstance1=newSuberType('Yvette',20);instance1.colors.push('黄色');console.log(instance1.colors);//['粉色','蓝色','绿色','黄色']instance1.sayName();//Yvetteletinstance2=newSuberType('Jack',22);console.log(instance2.colors);//['pink','blue','green']instance2.sayName();//Jack缺点:在任何情况下,超类型构造函数都会被调用两次:一次是在创建子类型原型时,一次是在创建子类型原型时在构造函数中创建。优点:可以给超类传递参数,每个实例都有自己的属性,实现函数重用。定义类型。functionobject(o){functionF(){}F.prototype=o;returnnewF();}在object()函数内部,首先创建一个临时构造函数,然后将传入的对象作为this构造函数的原型,最终返回这个临时类型的新实例,本质上object()执行传入对象的浅表副本。ECMAScript5通过添加Object.create()方法标准化了原型继承。此方法接受两个参数:一个用作新对象原型的对象和(可选)一个为新对象定义附加属性的对象(它可以覆盖原型对象上的同名属性)。在传入参数的情况下,Object.create()和object()方法的行为相同。varperson={name:'Yvette',hobbies:['reading','photography']}varperson1=Object.create(person);person1.name='Jack';person1.hobbies.push('coding');varperson2=Object.create(person);person2.name='Echo';person2.hobbies.push('running');console.log(person.hobbies);//['阅读','摄影','coding','running']console.log(person1.hobbies);//['reading','photography','coding','running']不需要创建构造函数,让一个对象交互即可在对象保持相似的情况下,原型继承就足够了。缺点:和实现继承的原型链一样,包含引用类型值的属性会被所有实例共享。29.寄生继承寄生继承是一种与原型继承密切相关的思想。寄生继承的思想类似于寄生构造函数和工厂模式,即创建一个只用于封装继承过程的函数,在内部以某种方式增强对象,最后表现得像它真的做了所有的事情一样工作。返回对象。functioncreateAnother(original){varclone=object(original);//通过调用函数创建一个新对象clone.sayHi=function(){//以某种方式增强这个对象console.log('hi');};returnclone;//返回这个对象}varperson={name:'Yvette',hobbies:['reading','photography']};varperson2=createAnother(person);person2.sayHi();//hi基于person返回一个新的对象---person2,这个新对象不仅拥有person的所有属性和方法,还有自己的sayHi()方法。在考虑对象而不是自定义类型和构造函数时,寄生继承也是一种有用的模式。缺点:使用寄生继承给对象添加功能会因无法实现功能重用而效率低下。和实现继承的原型链一样,包含引用类型值的属性会被所有实例共享。30.寄生组合继承所谓寄生组合继承,是指通过原型链的混合形式,通过借用构造函数和继承方法来继承属性。所需要的只是超类型原型的副本,本质上是使用寄生继承从超类型的原型继承并将结果分配给子类型的原型。寄生组合继承的基本模式如下:functioninheritPrototype(subType,superType){varprototype=object(superType.prototype);//createobjectprototype.constructor=subType;//enhanceobjectsubType.prototype=prototype;//specifyobject}第一步:创建超类型原型的副本第二步:将构造函数属性添加到创建的副本第三步:赋值将新创建的对象赋值给子类型的原型此时,我们可以调用inheritPrototype来替换语句为子类型原型赋值:functionSuperType(name){this.name=name;this.colors=['pink','blue','green'];}//...codefunctionSuberType(name,age){SuperType.call(this,name);this.age=age;}inheritPrototype(SuberType,SuperType);//...代码优势:超类构造函数只调用一次,效率更高。避免在SuberType.prototype上创建不必要的冗余属性,同时原型链可以保持不变。因此,寄生组合继承是引用类型最合理的继承范式。ES6继承类可以通过extends关键字来继承,如:classSuperType{constructor(age){this.age=age;}getAge(){console.log(this.age);}}classSubTypeextendsSuperType{constructor(age,name){super(age);//调用父构造函数(x,y)this.name=name;}getName(){console.log(this.name);}}letinstance=newSubType(22,'刘晓曦');instance.getAge();//22对于ES6类,有以下几点需要说明:类的数据类型是函数,类本身指向构造函数。console.log(typeofSuperType);//functionconsole.log(SuperType===SuperType.prototype.constructor);//真正类内部定义的所有方法都是不可枚举的。(ES5原型上的方法默认是可枚举的)Object.keys(SuperType.prototype);构造函数方法是类的默认方法。当通过new命令生成对象实例时,会自动调用该方法。一个类必须有一个构造方法,如果没有显式定义,默认会添加一个空的构造方法。不能像构造函数那样直接调用类,会抛出错误。在使用extends关键字实现继承时,需要说明一点:子类必须在构造函数中调用super方法,否则新建实例时会报错。如果没有子类没有定义构造方法,那么默认会添加这个方法。在子类的构造函数中,this关键字只能在调用super之后使用,否则会报错。这是因为子类实例的构造是基于父类实例的,只有super方法才能调用父类实例。类SubType扩展SuperType{constructor(...args){super(...args);}}参考文章:[1]CSS-ClearFloating[2]JS函数柯里化详解[3]JavaScript数组去重感谢各位朋友,您愿意花宝贵的时间阅读本文。如果这篇文章给了你一点帮助或者启发,请不要吝啬你的点赞和star。您的肯定是我前进的最大动力。https://github.com/YvetteLau/...推荐关注我公众号:
