js继承方法及优缺点总结
整理《javascript高级程序设计》中继承的方法及其优缺点。
1.原型链
ECMAScript中描述了原型链的概念,并将原型链视为实现继承的主要方法。
原型链继承的基本思想是利用原型使一个引用类型继承另一个引用类型的属性和方法。
简单回顾一下构造函数、原型和实例之间的关系:每个构造函数都有一个原型对象,原型对象包含一个指向构造函数的指针,而实例包含一个指向原型对象的内部指针。
那么,如果我们让原型对象等于另一种类型的实例,会发生什么呢?显然,此时的原型对象将包含指向另一个原型的指针,相应地,另一个原型也包含指向另一个构造函数的指针。如果另一个原型是另一种类型的实例,那么上述关系仍然成立,实例和原型的链就是通过这样的渐进步骤形成的。这是原型链的基本概念。
function SuperType(){ this . property=true;} SuperType . prototype . GetSuperVaLue=function(){ return this . property;} function SubType(){ this . SubParety=false;} subtype . prototype=new SuperType();subtype . prototype . getsubvalue=function(){ return this . subproperty;};var实例=new SubLe();console . log(instance . GetSuperVaLue());//true上面的代码定义了两种类型:SuperType和SuperType。每种类型都有一个属性和一个方法。它们之间的主要区别是SubType继承SuperType,而继承是通过创建SuperType的实例并将其分配给SubType.prototype来实现的.实现的本质是重写原型对象,并用新类型的实例替换它。换句话说,原来存在于SuperType实例中的所有属性和方法现在都存在于SubType.prototype中,在建立继承关系后,我们给SubType.prototype增加了一个方法,在继承SuperType属性和方法的基础上增加了一个新的方法
请注意,instance.constructor现在指向SuperType,因为SubType.prototype中的构造函数已经被重写。实际上,不是重写SubType原型的构造函数属性,SubType的原型指向另一个对象——SuperType的原型,这个原型对象的构造函数属性指向SuperType
不要忘记默认原型
事实上,前面例子中显示的原型链短了一个环节。我们知道,所有的引用类型默认都是继承Object的,这种继承是通过原型链实现的。请记住,所有函数的默认原型都是Object的一个实例,因此默认原型包含一个指向Object.prototype的内部指针。这是所有自定义类型都继承诸如toString()和valueOf()等默认方法的根本原因
原型链问题
虽然原型链很强大,可以用来继承,但是也存在一些问题。其中,主要问题来自包含引用类型值的原型
function SuperType(){ this . colors=[' red ',' blue ',' green '];}function SubType(){}//继承supertypesubtypeprototype=new super type();var instance 1=new SubLe();instance 1 . colors . push(' black ');console . log(instance 1 . colors);//'红、蓝、绿、黑' var instance 2=new SubType();console . log(instance 2 . colors);//‘红、蓝、绿、黑’原型链的第二个问题是在创建子类型的实例时,参数不能传递给超类型的构造函数。实际上,应该说,没有办法在不影响所有对象实例的情况下将参数传递给超类型构造函数。有鉴于此,再加上前面讨论的原型中引用类型值引起的问题,原型链在实践中很少单独使用
2.借用构造函数
在子类型构造函数中调用超类型构造函数
function SuperType(){ this . colors=[' red ',' blue ',' green '];} function SubType(){ SuperType . call(this);} var instance 1=new SubLe();instance 1 . colors . push(' black ');console . log(instance 1 . colors);//'红、蓝、绿、黑' var instance 2=new SubType();console . log(instance 2 . colors);//‘红、蓝、绿’通过使用call()方法(或者也可以使用apply()方法),我们实际上在新创建的SubType实例的环境中调用SuperType构造函数。这样,在SuperType()函数中定义的所有对象初始化代码都将在新的SuperType对象上执行。因此,每个子类型实例都有自己的颜色属性副本
对于原型链来说,借用构造函数有一个很大的优势,就是参数可以传递给子类型构造函数中的超类型构造函数
function SuperType(){ this . colors=[' red ',' blue ',' green '];} function SubType(){ SuperType . call(this);} var instance 1=new SubLe();instance 1 . colors . push(' black ');console . log(instance 1 . colors);//'红、蓝、绿、黑' var instance 2=new SubType();console . log(instance 2 . colors);//‘红、蓝、绿’借用构造函数:
方法都是在构造函数中定义的,所以没有办法重用函数。此外,超类型原型中定义的方法对于子类型是不可见的,因此,所有类型只能使用构造函数模式
3.组合遗传
组合继承,有时也称伪经典继承,是指将原型链与借用构造函数的技术相结合,使其优势得到充分发挥的继承模式。背后的思想是用原型链继承原型属性和方法,借用构造函数继承实例属性。这样,不仅可以通过在原型上定义方法来实现功能重用,而且可以保证每个实例都有自己的属性
函数SuperType(name){ this . name=name;this.colors=['red ',' blue ',' green '];} SuperType . prototype . SayName=function(){ console . log(this . name);};函数SubType(名称,年龄){ //继承属性SuperType.call(this,name);this.age=年龄;}//继承方法subtype . prototype=new SuperType();subtype . prototype . Sayage=function(){ console . log(this . age);};var instance 1=new SubType(' Nicholas ',29);instance 1 . colors . push(' black ');console . log(instance 1 . colors);//'红、蓝、绿、黑' instance 1 . say name();//‘尼古拉斯’;instance 1 . SayAge();//29 var instance 2=new SubType(' Greg ',27);console . log(instance 2 . colors);//'红、蓝、绿' instance 2 . SayName();//‘格雷格’;instance 2 . SayAge();//27组合继承避免了原型链和借用构造函数的缺陷,结合了它们的优点,成为JavaScript中最常用的继承模式。此外,instanceof和isPrototypeOf还可以用来识别基于组合继承创建的对象。
无论如何,超类型构造函数被调用两次:一次在创建子类型原型时,另一次在子类型构造函数中
4.原型继承
该方法不使用严格意义上的构造函数。借助prototype,您可以基于现有对象创建新对象,而无需创建自定义类型
函数对象(o){函数F()} { F . prototype=o;返回新的F();}var person={ name: 'Nicholas ',friends 3360[' Shelby ',' Court ',' Van ']};var other PeRsoN=object(person);另一个人。另一个人。朋友。推(罗伯);var yetotherperson=object(person);然而另一个人。然而另一个人。朋友。推(“芭比”);console . log(person . friends);//'shelby,court,van,rob,Barbie的ecmascript5通过添加Object.create()方法来标准化原型继承。此方法采用两个参数:一个用作新对象原型的对象,以及(可选)一个为新对象定义附加属性的对象。传入参数时,Object.create()和Object()方法的行为相同。Object.create()方法的第二个参数的格式与Object.defineProperties()方法的格式相同:每个属性都由自己的描述符定义。以这种方式指定的任何属性都会重写原型对象上同名的属性
var person={ name: 'Nicholas ',friends 3360[' Shelby ',' Court ',' Van ']};var other PeRsoN=object . create(person,{ name : { value : ' Greg ' } });console . log(ThEre PeRsoN . name);//‘Greg’Prototype继承在不需要创建构造函数,只想保持一个对象与另一个对象相似的时候是完全胜任的。但是,不要忘记包含引用类型值的属性将总是共享相应的值,就像使用原型模式一样
5.寄生遗传
创建一个只封装继承过程的函数,它以某种方式在内部增强对象,最后返回对象,就好像它真的完成了所有工作一样。
函数create other(original){ var clone=object . create(original);//通过调用函数来创建一个新的对象clone . sayhi=function(){//以某种方式增强这个对象console . log(' hi ');};返回克隆;//返回此对象}在本例中,createAnother()函数接收一个参数,该参数是将作为新对象基础的对象。然后,将这个对象(原始的)传递给object()函数,并将返回的结果分配给clone。然后给克隆对象添加一个新的方法sayHi(),最后返回克隆对象。您可以使用createAnother()函数,如下所示:
var person={ name: 'Nicholas ',friends 3360[' Shelby ',' Court ',' Van ']};var other person=create other(person);另一个人。sayHi();//“hi”本例中的代码返回一个基于Person的新对象——anotherPerson。新对象不仅具有人的所有属性和方法,还具有自己的sayHi()方法
使用寄生继承向对象添加函数会降低效率,因为不能重用函数。这类似于构造函数模式
6.寄生组合遗传
如前所述,组合继承是JavaScript最常用的继承模式;然而,它也有自己的缺点。组合继承最大的问题是超类型构造函数在任何情况下都会被调用两次:一次在创建子类型原型时,另一次在子类型构造函数内部。是的,子类型最终将包含超类型对象的所有实例属性,但是我们在调用子类型构造函数时必须覆盖这些属性。让我们看看下面这个组合继承的例子
函数SuperType(name){ this . name=name;this.colors=['red ',' blue ',' green '];} SuperType . prototype . SayName=function(){ console . log(this . name);};function SubType(姓名、年龄){ SuperType.call(this,姓名);//第二次调用SuperType(). age=age;} subtype . prototype=new SuperType();//第一次调用super type()subtype . prototype . constructor=subtype;subtype . prototype . Sayage=function(){ console . log(this . age);};第一次调用SuperType构造函数时,SubType.prototype会得到两个属性:名称和颜色;它们都是SuperType的实例属性,但是现在它们在SuperType的原型中。当调用SubType构造函数时,将再次调用SubType构造函数。这一次,实例属性名称和颜色是在新对象上创建的,因此这两个属性屏蔽了原型中具有相同名称的两个属性
如上图所示,有两组名称和颜色属性:一组在实例上,另一组在SubType原型中。这是两次调用SuperType构造函数的结果。幸运的是,我们找到了解决这个问题的办法:——寄生组合遗传。
寄生组合继承是指借用构造函数继承属性,混合原型链继承方法。
它背后的基本思想是,我们不必为了指定子类型的原型而调用超类型的构造函数。我们只需要一份超类型原型的拷贝。本质上,寄生继承用于继承超类型的原型,然后将结果赋给子类型的原型。寄生组合遗传的基本模式如下
函数inheritPrototype(subType,SuperType){ var prototype=object . create(SuperType . prototype);//创建对象原型. prototype.constructor=subType//增强对象subType.prototype=prototype//指定对象}本例中的inheritPrototype()函数实现了最简单形式的寄生组合继承。这个函数有两个参数:子类型构造函数和超类型构造函数。在函数内部,第一步是创建超类型原型的副本。第二步是将构造函数属性添加到创建的副本中,以弥补由于重写原型而丢失的默认构造函数属性。最后一步是将新创建的对象(即副本)分配给子类型的原型。通过这种方式,我们可以用调用inheritPrototype()函数的语句替换上一个示例中给子类型原型赋值的语句
函数SuperType(name){ this . name=name;this.colors=['red ',' blue ',' green '];} SuperType . prototype . SayName=function(){ console . log(this . name);};function SubType(姓名、年龄){ SuperType.call(this,姓名);this.age=年龄;}inheritPrototype(SubType,SuperType);subtype . prototype . Sayage=function(){ console . log(this . age);};这个例子的效率在于它只调用SuperType构造函数一次,从而避免在SubType.prototype上创建不必要的冗余属性,同时原型链可以保持不变;因此,instanceof和isPrototypeOf()也可以正常使用。开发人员普遍认为寄生组合继承是引用类型最理想的继承范式。
摘要
以上就是本文的全部内容。希望本文的内容对大家的学习或工作有一定的参考价值。谢谢你的支持。
版权声明:js继承方法及优缺点总结是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。