ホームページ >ウェブフロントエンド >jsチュートリアル >JavaScript_javascriptスキルの継承の3つの方法と例
JavaScript はオブジェクト指向言語ですが、その継承メカニズムは他の従来のオブジェクト指向言語とは元々異なり、プロトタイプベースの継承メカニズムですが、このメカニズムの下では依然として継承が存在します。これを実装するにはいくつかの異なる方法があります。
方法 1: クラスの継承
いわゆるクラス継承とは、伝統的なオブジェクト指向言語を模倣した継承方法を指します。コードは次のとおりです。
最初に親クラス (またはスーパークラス) を定義します:
function Person(name){ this.name=name; } Person.prototype.getName=function(){ return this.name; };
親クラス person の属性はコンストラクターで定義され、それを継承するサブクラスの name 属性がこの属性と共有せず、getName メソッドがそのサブクラスにのみマウントされることが保証されます。サブクラスの複数のインスタンスがこのメソッド本体を共有できるようにすることで、メモリを節約できます (複数のインスタンスの場合、新しいインスタンスが作成されるたびに、これらのインスタンスの getName メソッドが同じメモリ空間を参照することが保証されます。別のスペースより)。
次のように継承メソッド拡張を定義します:
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; } }
このメソッドでは、まず新しいクラス F を作成し、そのプロトタイプを親クラスのプロトタイプにし、サブクラスのプロトタイプがクラス F のインスタンスを指すようにすることで、親クラスを継承するという目的を達成します。同時に、サブクラスのプロトタイプが変更されているため、変更されたプロトタイプのコンストラクター属性がサブクラスを指すようになり、サブクラスがコンストラクターを持ち、同時に superClass 属性がサブクラスにマウントされます。これにより、サブクラスは親クラスを呼び出すことができ、サブクラスと親クラスの間の関係が確立されます。
次のように、親クラス person を継承するサブクラス Author を定義します。
function Author(name,books){ Author.superClass.constructor.call(this,name); this.book=books; } extend(Author,Person); Author.prototype.getBooks=function(){ return this.book; }
この継承メソッドは、明らかに従来のオブジェクト指向言語と同様のクラス継承を使用します。利点は、従来のオブジェクト指向の概念に慣れているプログラマにとって理解しやすいことです。欠点は、プロセスがより複雑であることです。サブクラスにも独自のコンストラクターとプロトタイプがあり、サブクラスと親クラスのプロパティは完全に分離されているため、メモリ消費量はわずかに大きくなります。たとえ 2 つの値が同じであっても、同じメモリを共有することはできません。
方法 2: プロトタイプの継承
最初に親クラスを定義します。これは、コンストラクターの使用を意図的に模倣して定義するのではなく、オブジェクト リテラルを直接使用して、親クラスであるオブジェクトを定義します。
var Person={ name:'default name', getName:function(){ return this.name; } } ;
次に、次のように、親クラスからのサブクラスの継承を実現するクローン メソッドを定義します。
function clone(obj){ function F(){} F.prototype=obj; return new F(); }
最後に、次のように、サブクラスは clone 関数を通じて親クラスを継承します。
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的属性和方法都是通过“继承”得来的,从实际上来讲,它实现了多重继承。