ホームページ  >  記事  >  ウェブフロントエンド  >  JavaScript のクラス継承_javascript のヒント

JavaScript のクラス継承_javascript のヒント

WBOY
WBOYオリジナル
2016-05-16 18:15:191005ブラウズ

JavaScript Inheritance

DouglasCrockford
www.crockford.com

And you think you're so clever and classless and free
--John Lennon

JavaScript一种没有类的,面向对象的语言,它使用原型继承来代替类继承。这个可能对受过传统的面向对象语言(如C++和Java)训练的程序员来说有点迷惑。JavaScript的原型继承比类继承有更强大的表现力,现在就让我们来看看。

Java

JavaScript

强类型

弱类型

静态

动态

基于类

基于原型

函数

构造器

函数

方法

函数

まず最初に、なぜ私たちは継承をそれほど気にするのでしょうか?主な理由は 2 つあります。 1つ目はタイプ有利です。私たちは、言語システムが同様の型参照変換キャストを自動的に実行できることを望んでいます。小規模な型安全性は、プログラムがオブジェクト参照を明示的に変換することを必要とする型システムから得ることができます。これは強く型付けされた言語の最も重要な点ですが、クラス参照にキャストが必要ない JavaScript のような弱い型付け言語とは何の関係もありません。

2 番目の理由は、コードの再利用です。プログラムでは、多くのオブジェクトが同じメソッドを実装していることがよくあります。クラスを使用すると、単一の定義セットを使用してオブジェクトを作成できます。オブジェクトが他のオブジェクトに含まれるオブジェクトを含むこともよくありますが、違いは少数のメソッドの追加または変更のみです。これにはクラスの継承が非常に便利ですが、プロトタイプの継承はさらに便利です。

これを実証するために、通常のクラス言語のようにコードを記述できるようにする小さな「スイートスポット」を紹介します。次に、クラス言語にはない便利なパターンをいくつか示します。最後に、これらの「デザート」について説明します。
クラス継承
まず、Parenizor クラスを作成します。このクラスには、メンバー値の get メソッドと set メソッド、および値を括弧で囲む toString メソッドがあります。

コードをコピー コードは次のとおりです。

function Parenizor(value) {
this.setValue (値);
}
Parenizor.method('setValue', function (value) {
this.value = value;
return this;
}); >Parenizor.method('getValue', function () {
return this.value;
});
Parenizor.method('toString', function () {
return '(' this .getValue( ) ')';
});

この構文は役に立たないかもしれませんが、クラスの形式を確認するのは簡単です。メソッドメソッドはメソッド名と関数を受け取り、それらをパブリック メソッドとしてクラスに追加します。
これで、

コードをコピーします コードは次のとおりです:
myParenizor = new Parenizor( 0);
myString = myParenizor.toString();

予想通り、myString は "(0)" です。
ここで、Parenizor を継承する別のクラスを作成します。値がゼロまたは空の場合に toString メソッドが「-0-」を生成することを除いて、基本的に同じです。

コードをコピーします コードは次のとおりです。
function ZParenizor(value) {
this.setValue (値);
ZParenizor.inherits(Parenizor);
ZParenizor.method("e;toString"e;, function () {
if (this.getValue() ) {
return this.uber('toString');
}
return "-0-"
}); 継承メソッドは次のようになります。 Javaは拡張します。 uber メソッドは Java の super に似ています。メソッドが親クラスのメソッドを呼び出すようにします (予約語との競合を避けるために名前が変更されています)。
次のように記述できます



コードをコピーします
コードは次のとおりです: myZParenizor = new ZParenizor( 0); myString = myZParenizor.toString();
今回、myString にはクラスがありません。しかし、この目的はプログラムで達成できます。
多重継承
関数のプロトタイプオブジェクトを操作することで多重継承を実現できます。混合多重継承は実装が難しく、名前の衝突のリスクが生じる可能性があります。 JavaScript では混合多重継承を実装できますが、この例ではスイス継承と呼ばれるより標準化された形式を使用します。
値が指定された範囲内にあるかどうかを確認する setValue メソッドを持つ NumberValue クラスがあるとします。内で実行され、必要に応じて例外がスローされます。 ZParenizor の setValue メソッドと setRange メソッドが必要なだけです。確かに、その toString メソッドは必要ありません。このようにして、次のように記述します。




コードをコピー

コードは次のとおりです: ZParenizor。 swiss(NumberValue, 'setValue', 'setRange');
これにより、必要なメソッドのみが追加されます。
寄生継承
これは、ZParenizor クラスを記述する別の方法です。これは Parenizor から継承しませんが、Parenizor コンストラクターを呼び出し、結果を変更し、最終的に結果を返すコンストラクターを作成します。このコンストラクターは、パブリック メソッドではなく特権メソッドを追加します。
コードをコピーします コードは次のとおりです。

function ZParenizor2(value) {
var self = new Parenizor(value);
self.toString = function () {
if (this.getValue()) {
return this.uber('toString'); 🎜>return "-0-"
};
return self;


クラス継承は「である」関係ですが、寄生継承は関係です。 「元々は…でしたが、今は…」という関係について。コンストラクターはオブジェクトの構築において大きな役割を果たします。 (super キーワードの代わりに) uber が特権メソッドに対して引き続き機能することに注意してください。
クラス拡張
JavaScript の動的な性質により、既存のクラスにメソッドを追加または置き換えることができます。いつでもメソッドを呼び出すことができます。いつでもクラスを延長できます。このように継承は機能しません。したがって、Java の extends (拡張とも呼ばれますが、同じものではありません) との混同を避けるために、この状況を「クラス拡張」と呼びます。
オブジェクト拡張
静的オブジェクト指向言語では、あるオブジェクトを別のオブジェクトと異なるものにしたい場合は、新しいクラスを作成する必要があります。しかし、JavaScript では、新しいクラスを作成しなくても、個々のオブジェクトにメソッドを追加できます。作成できるクラスの数を最小限に抑え、クラスをより簡単に作成できるため、これには大きな効果があります。 JavaScript オブジェクトはハッシュ テーブルのようなものだと考えてください。いつでも新しい値を追加できます。値が関数の場合はメソッドになります。
つまり、上記の例では、ZParenizor クラスはまったく必要ありません。インスタンスに簡単な変更を加えるだけです。



コードをコピーします コードは次のとおりです。 myParenizor = new Parenizor(0); 🎜>myParenizor .toString = function () {
if (this.getValue()) {
return this.uber('toString');
return "-0-"; 🎜>} ;
myString = myParenizor.toString();


継承を使用せずに toString メソッドを myParenizor インスタンスに追加しました。言語には型がないため、個々のインスタンスを進化させることができます。
小さなデザート
上記の例を機能させるために、4 つの「デザート」メソッドを作成しました。まず、メソッドメソッドはクラスにインスタンスメソッドを追加できます。



コードをコピー

これにより、Function.prototype にパブリック メソッドが追加されます。クラスによって拡張でき、すべての関数で使用できます。パラメータとして名前と関数を受け取ります。
これを返します。値を返さないメソッドを作成する場合、通常はこれを返すようにします。これにより、連鎖ステートメントが形成される可能性があります。
以下は、継承メソッドです。これは、あるクラスが別のクラスを継承することを示します。これは両方のクラスを定義した後に定義する必要がありますが、メソッドの継承の前に呼び出す必要があります。



コードをコピー


コードは次のとおりです。while (t) {
v = v.constructor.prototype;
t -= 1; 🎜>f = v[名前];
} else {
f = p[名前];
if (f == this[名前]) {
f = v[名前]; 🎜>}
}
d = 1;
r = f.apply(this, Array.prototype.slice.apply(arguments, [1])); 🎜>r を返します。
});
次に、Function クラスを拡張しましょう。親クラスのインスタンスを追加し、それを新しいプロトタイプにします。コンストラクター フィールドも変更する必要があり、uber メソッドを追加します。
uber メソッドは、独自のプロトタイプでメソッドを探します。これは、寄生継承またはクラス拡張のケースです。クラスから継承している場合は、親のプロトタイプで関数を見つける必要があります。 return ステートメントは、関数の apply メソッドを呼び出して関数を呼び出しますが、これを明示的に設定してパラメーターを渡します。パラメータ (存在する場合) は引数配列から取得されます。残念ながら、引数の配列は実際の配列ではないため、apply を再度使用して配列内のスライス メソッドを呼び出す必要があります。
最後に、スイスメソッド
コードをコピー コードは次のとおりです:

関数.method(' swiss', function (parent) {
for (var i = 1; i < argument.length; i = 1) {
var name = argument[i];
this. prototype[name] =parent.prototype[name];
}
return this;

スイスのメソッドは各パラメータをループします。名前ごとに、親のプロトタイプから新しいクラスのプロトタイプにメンバーをコピーします。
概要
JavaScript はクラス言語のように使用できますが、非常に独特なレベルの表現も備えています。クラス継承、スイス継承、寄生継承、クラス拡張、オブジェクト拡張について見てきました。この一連のコード再利用パターンはすべて、小さくて単純だと常に考えられてきたこの JavaScript 言語から来ています。
クラスオブジェクトは「ハード」です。 「ハード」オブジェクトにメンバーを追加する唯一の方法は、新しいクラスを作成することです。 JavaScript では、オブジェクトは「ソフト」です。 「ソフト」オブジェクトにメンバーを追加するには、値を割り当てるだけです。
JavaScript のクラスは非常に柔軟であるため、より複雑なクラスの継承を考えることもできます。ただし、深い継承は適していません。浅い継承はより効率的で表現しやすくなります。
声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。