ホームページ > 記事 > ウェブフロントエンド > JavaScript継承の主な6パターンまとめ_JavaScriptスキル
1. プロトタイプチェーン
function SuperType(){ this.property = true; } SuperType.prototype.getSuperValue = function(){ return this.property; }; function SubType(){ this.subproperty = false; } //继承了SuperType SubType.prototype = new SuperType(); SubType.prototype.getSubValue = function (){ return this.subproperty; }; var instance = new SubType(); alert(instance.getSuperValue()); //true
実装の本質は、プロトタイプ オブジェクトをオーバーライドし、新しい型のインスタンスに置き換えることです。
2. コンストラクターを借用します
function SuperType(){ this.colors = ["red", "blue", "green"]; } function SubType(){ //继承了SuperType SuperType.call(this); } var instance1 = new SubType(); instance1.colors.push("black"); alert(instance1.colors); //"red,blue,green,black" var instance2 = new SubType(); alert(instance2.colors); //"red,blue,green"
コンストラクターを借用するだけの場合、コンストラクター パターンの問題を回避することはできません。メソッドはすべてコンストラクター内で定義されているため、関数の再利用は問題外です。さらに、スーパータイプのプロトタイプで定義されたメソッドはサブタイプには表示されないため、すべてのタイプはコンストラクター パターンのみを使用できます。これらの問題を考慮すると、コンストラクターを借用する手法が単独で使用されることはほとんどありません。
3. 組み合わせの継承
function SuperType(name){ this.name = name; this.colors = ["red", "blue", "green"]; } SuperType.prototype.sayName = function(){ alert(this.name); }; function SubType(name, age){ //继承属性 SuperType.call(this, name); this.age = age; } //继承方法 SubType.prototype = new SuperType(); SubType.prototype.constructor = SubType; SubType.prototype.sayAge = function(){ alert(this.age); }; var instance1 = new SubType("Nicholas", 29); instance1.colors.push("black"); alert(instance1.colors); //"red,blue,green,black" instance1.sayName(); //"Nicholas"; instance1.sayAge(); //29 var instance2 = new SubType("Greg", 27); alert(instance2.colors); //"red,blue,green" instance2.sayName(); //"Greg"; instance2.sayAge(); //27
この例では、SuperType コンストラクターは、名前と色という 2 つのプロパティを定義します。 SuperType のプロトタイプでは、sayName() メソッドが定義されています。 SubType コンストラクターは、SuperType コンストラクターを呼び出すときに name パラメーターを渡し、独自の属性 age を定義します。次に、SuperType のインスタンスが SubType のプロトタイプに割り当てられ、メソッド SayAge() が新しいプロトタイプに定義されます。このようにして、2 つの異なる SubType インスタンスが、colors プロパティを含む独自のプロパティを持つことができ、同じメソッドを使用できます。
結合継承は、プロトタイプ チェーンと借用コンストラクターの欠点を回避し、それらの利点を組み合わせて、JavaScript で最も一般的に使用される継承パターンになります。さらに、instanceof と isPrototypeOf() を使用して、結合された継承に基づいて作成されたオブジェクトを識別することもできます。
4. プロトタイプの継承
function object(o){ function F(){} F.prototype = o; return new F(); }
object() 関数内では、最初に一時コンストラクターが作成され、次に渡されたオブジェクトがこのコンストラクターのプロトタイプとして使用され、最後にこの一時型の新しいインスタンスが返されます。基本的に、object() は、渡されたオブジェクトの浅いコピーを実行します。次の例を考えてみましょう。
var person = { name: "Nicholas", friends: ["Shelby", "Court", "Van"] }; var anotherPerson = object(person); anotherPerson.name = "Greg"; anotherPerson.friends.push("Rob"); var yetAnotherPerson = object(person); yetAnotherPerson.name = "Linda"; yetAnotherPerson.friends.push("Barbie"); alert(person.friends); //"Shelby,Court,Van,Rob,Barbie"
Crockford が提唱したプロトタイプ継承では、別のオブジェクトの基礎として機能するオブジェクトが必要です。そのようなオブジェクトがある場合は、それを object() 関数に渡し、特定のニーズに応じて取得したオブジェクトを変更できます。この例では、person オブジェクトを別のオブジェクトの基礎として使用できるため、それを object() 関数に渡し、新しいオブジェクトを返します。この新しいオブジェクトはそのプロトタイプとして person を使用するため、そのプロトタイプには基本型値プロパティと参照型値プロパティが含まれます。これは、person.friends が person によって所有されているだけでなく、anotherperson と YetAnotherperson によっても共有されていることを意味します。事実上、これは person オブジェクトのコピーをさらに 2 つ作成することと同じです。
ECMAScript 5 は、新しい Object.create() メソッドを通じてプロトタイプの継承を標準化します。このメソッドは 2 つのパラメータを受け入れます。新しいオブジェクトのプロトタイプとして使用されるオブジェクトと、(オプションで) 新しいオブジェクトの追加プロパティを定義するオブジェクトです。 Object.create() メソッドは、1 つのパラメーターが渡された場合、object() メソッドと同じように動作します。
var person = { name: "Nicholas", friends: ["Shelby", "Court", "Van"] }; var anotherPerson = Object.create(person); anotherPerson.name = "Greg"; anotherPerson.friends.push("Rob"); var yetAnotherPerson = Object.create(person); yetAnotherPerson.name = "Linda"; yetAnotherPerson.friends.push("Barbie"); alert(person.friends); //"Shelby,Court,Van,Rob,Barbie"
Object.create() メソッドの 2 番目のパラメーターは、Object.defineProperties() メソッドの 2 番目のパラメーターと同じ形式です。各プロパティは独自の記述子を通じて定義されます。この方法で指定されたプロパティは、プロトタイプ オブジェクトの同じ名前のプロパティをオーバーライドします。例:
var person = { name: "Nicholas", friends: ["Shelby", "Court", "Van"] }; var anotherPerson = Object.create(person, { name: { value: "Greg" } }); alert(anotherPerson.name); //"Greg"
Object.create() メソッドをサポートするブラウザには、IE9、Firefox 4、Safari 5、Opera 12、Chrome などがあります。
プロトタイプ継承は、コンストラクターを作成するために多大な労力を費やす必要がなく、あるオブジェクトを別のオブジェクトと同様に保ちたいだけの場合に完全に適切です。ただし、参照型の値を含むプロパティは、プロトタイプ パターンを使用する場合と同様に、対応する値を常に共有することを忘れないでください。
5. 寄生継承
寄生継承はプロトタイプ継承と密接に関連するアイデアであり、クロックフォードによっても広められました。寄生継承の考え方は、寄生コンストラクターやファクトリ パターンに似ています。つまり、継承プロセスをカプセル化するためだけに関数を作成し、オブジェクトを内部的に何らかの方法で強化し、最終的には実際にすべての作業を実行したかのように動作します。オブジェクトを返します。次のコードは、寄生継承パターンを示しています。
function createAnother(original){ var clone = object(original); //通过调用函数创建一个新对象 clone.sayHi = function(){ //以某种方式来增强这个对象 alert("hi"); }; return clone; //返回这个对象 }
この例では、createAnother() 関数は、新しいオブジェクトの基礎として使用されるオブジェクトである 1 つのパラメーターを受け取ります。次に、このオブジェクト (オリジナル) を object() 関数に渡し、返された結果を clone に割り当てます。次に、新しいメソッドsayHi()をクローン オブジェクトに追加し、最後にクローン オブジェクトを返します。 createAnother() 関数は次のように使用できます:
var person = { name: "Nicholas", friends: ["Shelby", "Court", "Van"] }; var anotherPerson = createAnother(person); anotherPerson.sayHi(); //"hi"
这个例子中的代码基于person 返回了一个新对象——anotherPerson。新对象不仅具有person的所有属性和方法,而且还有自己的sayHi()方法。
在主要考虑对象而不是自定义类型和构造函数的情况下,寄生式继承也是一种有用的模式。前面示范继承模式时使用的object()函数不是必需的;任何能够返回新对象的函数都适用于此模式。
使用寄生式继承来为对象添加函数,会由于不能做到函数复用而降低效率;这一
点与构造函数模式类似。
6.寄生组合式继承
前面说过,组合继承是JavaScript 最常用的继承模式;不过,它也有自己的不足。组合继承最大的问题就是无论什么情况下,都会调用两次超类型构造函数:一次是在创建子类型原型的时候,另一次是在子类型构造函数内部。没错,子类型最终会包含超类型对象的全部实例属性,但我们不得不在调用子类型构造函数时重写这些属性。再来看一看下面组合继承的例子。
function SuperType(name){ this.name = name; this.colors = ["red", "blue", "green"]; } SuperType.prototype.sayName = function(){ alert(this.name); }; function SubType(name, age){ SuperType.call(this, name); //第二次调用SuperType() this.age = age; } SubType.prototype = new SuperType(); //第一次调用SuperType() SubType.prototype.constructor = SubType; SubType.prototype.sayAge = function(){ alert(this.age); };
加粗字体的行中是调用SuperType 构造函数的代码。在第一次调用SuperType 构造函数时,SubType.prototype 会得到两个属性:name 和colors;它们都是SuperType 的实例属性,只不过现在位于SubType 的原型中。当调用SubType 构造函数时,又会调用一次SuperType 构造函数,这一次又在新对象上创建了实例属性name 和colors。于是,这两个属性就屏蔽了原型中的两个同名属性。图6-6 展示了上述过程。
如图6-6 所示,有两组name 和colors 属性:一组在实例上,一组在SubType 原型中。这就是调用两次SuperType 构造函数的结果。好在我们已经找到了解决这个问题方法——寄生组合式继承。
所谓寄生组合式继承,即通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。其背后的基本思路是:不必为了指定子类型的原型而调用超类型的构造函数,我们所需要的无非就是超类型原型的一个副本而已。本质上,就是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型。寄生组合式继承的基本模式如下所示。
function inheritPrototype(subType, superType){ var prototype = object(superType.prototype); //创建对象 prototype.constructor = subType; //增强对象 subType.prototype = prototype; //指定对象 }
这个示例中的inheritPrototype()函数实现了寄生组合式继承的最简单形式。这个函数接收两个参数:子类型构造函数和超类型构造函数。在函数内部,第一步是创建超类型原型的一个副本。第二步是为创建的副本添加constructor 属性,从而弥补因重写原型而失去的默认的constructor 属性。最后一步,将新创建的对象(即副本)赋值给子类型的原型。这样,我们就可以用调用inherit-Prototype()函数的语句,去替换前面例子中为子类型原型赋值的语句了,例如
function SuperType(name){ this.name = name; this.colors = ["red", "blue", "green"]; } SuperType.prototype.sayName = function(){ alert(this.name); }; function SubType(name, age){ SuperType.call(this, name); this.age = age; } inheritPrototype(SubType, SuperType); SubType.prototype.sayAge = function(){ alert(this.age); };
这个例子的高效率体现在它只调用了一次SuperType 构造函数,并且因此避免了在SubType.prototype 上面创建不必要的、多余的属性。与此同时,原型链还能保持不变;因此,还能够正常使用instanceof 和isPrototypeOf()。开发人员普遍认为寄生组合式继承是引用类型最理想的继承范式。
YUI 的YAHOO.lang.extend()方法采用了寄生组合继承,从而让这种模式首次出现在了一个应用非常广泛的JavaScript 库中。要了解有关YUI 的更多信息,请访问http://developer. yahoo.com/yui/。
以上所述就是本文的全部内容了,希望对大家学习javascript继承有所帮助。