Bien que JavaScript soit un langage orienté objet, son mécanisme d'héritage est différent des autres langages orientés objet traditionnels depuis le début. Il s'agit d'un mécanisme d'héritage basé sur un prototype, mais dans le cadre de ce mécanisme, l'héritage existe toujours. quelques façons différentes de mettre en œuvre cela.
Méthode 1 : Héritage de classe
Le soi-disant héritage de classe fait référence à la méthode d'héritage qui imite les langages orientés objet traditionnels. L'héritage et l'héritage sont des "classes". Le code est le suivant :
Définissez d'abord une classe parent (ou super classe) :
function Person(name){ this.name=name; } Person.prototype.getName=function(){ return this.name; };
Les attributs de la classe parent person sont définis dans le constructeur, ce qui garantit que l'attribut name de la sous-classe qui en hérite ne partage pas cet attribut avec elle, mais appartient uniquement à la sous-classe. La méthode getName est montée sur le. prototype pour laisser plusieurs instances de ses sous-classes partager ce corps de méthode, ce qui peut économiser de la mémoire. (Pour plusieurs instances, chaque fois qu'une nouvelle instance sort, il sera assuré que la méthode getName de ces instances fait référence au même espace mémoire. qu'un espace séparé).
Définissez une méthode d'héritage étendue comme suit :
function extend(subClass,superClass){ var F=function(){}; F.prototype=superClass.prototype; subClass.prototype=new F(); subClass.prototype.constructor=subClass; subClass.superClass=superClass.prototype; if(superClass.prototype.constructor==Object.prototype.constructor){ superClass.prototype.constructor=superClass; } }
Dans cette méthode, créez d'abord une nouvelle classe F, faites de son prototype le prototype de la classe parent et laissez le prototype de la sous-classe pointer vers une instance de la classe F, atteignant ainsi l'objectif d'hériter de la classe parent, en même temps, parce que le prototype de la sous-classe a été modifié, l'attribut constructeur du prototype modifié est pointé vers la sous-classe, de sorte qu'il ait un constructeur, et en même temps, un attribut superClass est monté sur la sous-classe, à travers lequel la sous-classe peut appeler la classe parent, établissant ainsi la relation entre la sous-classe et la classe parent.
Définissez une sous-classe Author pour hériter de la classe parent Person, comme suit :
function Author(name,books){ Author.superClass.constructor.call(this,name); this.book=books; } extend(Author,Person); Author.prototype.getBooks=function(){ return this.book; }
Ici, dans le constructeur de la sous-classe, le constructeur de la classe parent est appelé via son attribut superClass, et la méthode d'appel est utilisée pour convertir le pointeur this appelé par la méthode, afin que la sous-classe Author possède également (hérite ) les attributs de la classe parent, et en même temps La sous-classe a également son propre livre d'attributs, donc dans le constructeur, les livres de paramètres sont attribués au livre d'attributs pour atteindre l'objectif de construction. Utilisez la fonction extend pour hériter des propriétés et des méthodes sur le prototype de la classe parent Person (en fait, seules les méthodes sont héritées, car nous n'avons monté les méthodes que sur le prototype auparavant et les propriétés ont été définies dans le constructeur). Dans le même temps, Author dispose de sa propre méthode getBooks, qui est montée sur le prototype correspondant, atteignant ainsi l'objectif de s'étendre davantage sur la base de l'héritage.
Cette méthode d'héritage utilise évidemment l'héritage de classe similaire aux langages orientés objet traditionnels. L'avantage est qu'elle est facile à comprendre pour les programmeurs habitués aux concepts traditionnels orientés objet. L'inconvénient est que le processus est plus lourd et plus lourd. La consommation de mémoire est légèrement plus importante, car la sous-classe a également son propre constructeur et prototype, et les propriétés de la sous-classe et de la classe parent sont complètement isolées. Même si les deux ont la même valeur, elles ne peuvent pas partager la même mémoire.
Méthode 2 : Héritage prototypique
Définissez d'abord une classe parent. Cela n'imitera pas délibérément l'utilisation de constructeurs pour définir, mais utilisera directement des littéraux d'objet pour définir un objet, qui est la classe parent
var Person={ name:'default name', getName:function(){ return this.name; } } ;
Identique à la première méthode, cet objet a un nom d'attribut et une méthode getName.
Définissez ensuite une méthode de clonage pour réaliser l'héritage de la sous-classe de la classe parent, comme suit :
function clone(obj){ function F(){} F.prototype=obj; return new F(); }
Cette méthode de clonage crée un nouvel objet, pointe le prototype de l'objet vers la classe parent, qui est le paramètre obj, et renvoie l'objet en même temps.
Enfin, la sous-classe hérite de la classe parent via la fonction clone, comme suit :
var Author=clone(Person); Author.book=['javascript']; Author.showBook=function(){ return this.book; }
这里定义一个子类,通过clone函数继承父类Person,同时拓展了一个属性book,和一个方法showBook,这里该子类也拥有属性name,但是它和父类的name值是一样的,所以没有进行覆盖,如果不一样,可以采用
Author.name='new name';覆盖这个属性,从而得到子类的一个新的name属性值。
这种原型式继承相比于类式继承更为简单自然,同时如果子类的属性和父类属性值相同,可以不进行修改的话,那么它们两者其实共享的是同一段内存空间,如上面的name属性,缺点是对于习惯了传统面向对象的程序员难以理解,如果两者要进行选择的话,无疑是这种方式更为优秀一些。
既然javascript中采用基于原型的方式来实现继承,而且每个对象的原型只能指向某个特定的类的实例(不能指向多个实例),那么如何实现多重继承(即让一个类同时具有多个类的方法和属性,而且本身内部不自己定义这些方法和属性)?
在javascript设计模式中给出了一种掺元类(mixin class)的方式:
首先定义一个掺元类,用来保存一些常用的方法和属性,这些方法和属性可以通过拓展的方式添加到任何其他类上,从而被添加类就具有了该类的某些方法和属性,如果定义多个掺元类,同时添加给一个类,那么该类就是间接实现了“多重继承”,基于这种思想,实现如下:
掺元类定义:
var Mixin=function(){}; Mixin.prototype={ serialize:function(){ var output=[]; for(key in this){ output.push(key+":"+this[key]); } return output.join(','); } }
该掺元类具有一个serialize方法,用来遍历其自身,输出自身的属性和属性值,并且将他们以字符串形式返回,中间用逗号隔开。
定义一个扩充方法,用来使某个类经过扩充之后具备掺元类的属性或方法,如下:
function augment(receivingClass,givingClass){ if(arguments[2]){ for(var i= 2,len=arguments.length;i<len;i++){ receivingClass.prototype[arguments[i]]=givingClass.prototype[arguments[i]]; } } else { for(methodName in givingClass.prototype){ if(!receivingClass.prototype[methodName]){ receivingClass.prototype[methodName]=givingClass.prototype[methodName]; } } } }
该方法默认是两个参数,第一个参数是接受被扩充的类,第二个参数是掺元类(用来扩充其他类的类),还可以有其他参数,如果大于两个参数,后面的参数都是方法或者属性名,用来表示被扩充类只想继承掺元类的指定的属性或方法,否则默认继承掺元类的所有属性和方法,在该函数中,第一个if分支是用来继承指定属性和方法的情形,else分支是默认继承所有属性和方法的情形。该方法的实质是将掺元类的原型上的属性和方法都扩充(添加)到了被扩充类的原型上面,从而使其具有掺元类的属性和方法。
最后,使用扩充方法实现多重继承
augment(Author,Mixin); var author= new Author('js',['javascript design patterns']); alert(author.serialize());
这里定义了一个author的类,该类继承自Person父类,同时又具备掺元类Mixin的方法和属性,如果你愿意,可以定义N个掺元类用来扩充该类,它同样能够继承你定义的其他掺元类的属性和方法,这样就实现了多重继承,最后,author的serialize方法的运行结果如下:
你会发现该类同时具有person类,Author类,Mixin类的属性和方法,其中Person和Mixin的属性和方法都是通过“继承”得来的,从实际上来讲,它实现了多重继承。