ES6の微妙さ:継承(部1)

数週間前(記事は8月- およそTranslに書かれまし )オブジェクトコンストラクターを作成する些細なケースで、ES6の新しいクラスシステムについて説明しました。 このようなコードを作成する方法を示しました。

class Circle { constructor(radius) { this.radius = radius; Circle.circlesMade++; }; static draw(circle, canvas) { // Canvas drawing code }; static get circlesMade() { return !this._count ? 0 : this._count; }; static set circlesMade(val) { this._count = val; }; area() { return Math.pow(this.radius, 2) * Math.PI; }; get radius() { return this._radius; }; set radius(radius) { if (!Number.isInteger(radius)) throw new Error("Circle radius must be an integer."); this._radius = radius; }; } 

残念ながら(一部の人が気づいたように)、当時はES6クラスの全機能について話す時間はありませんでした。 従来のクラスシステム(JavaおよびC ++)と同様に、ES6では、あるクラスが別のクラスをベースとして継承する場合に継承が可能です。

この機能の機能を詳しく見てみましょう。

先に進む前に、プロパティの継承と動的なプロトタイプチェーンの機能を検討するのに時間を費やすことが役立ちます。

Javascriptの継承


オブジェクトを作成すると、その中にプロパティを配置できます。また、プロトタイプのプロパティも継承します。 Javascriptプログラマーは既存のObject.create APIに精通しているため、次のようなことが簡単にできます。

 var proto = { value: 4, method() { return 14; } } var obj = Object.create(proto); obj.value; // 4 obj.method(); // 14 


さらに、 protoと同じ名前でプロパティをobjに追加すると、元のプロパティとオーバーラップ( シャドウ )します。

 obj.value = 5; obj.value; // 5 proto.value; // 4 


継承の基本


これを念頭に置いて、クラスによって作成されたオブジェクトのプロトタイプチェーンを接続する方法を確認できます。 クラスを作成するとき、すべての静的メソッドを含むクラスの定義(定義)のconstructorメソッドに対応する新しい関数を作成することに注意してください。 また、作成した関数のプロパティpropertyなり、クラスのインスタンス(インスタンス)のすべてのメソッドを含むオブジェクトを作成します。 すべての静的プロパティを継承する新しいクラスを作成するには、スーパークラス関数オブジェクトから新しい関数オブジェクトを強制的に継承する必要があります。 同様に、クラスインスタンスメソッドが継承されるように、新しい関数のprototypeオブジェクトをスーパークラスのprototypeから継承する必要があります。

これはかなり密な説明です。 新しい構文を使用せずにオブジェクトを接続する方法を例で示してみましょう。その後、簡単に追加して、見た目を美しくします。

前の例を続けて、サブクラス( subclass )を作成するShapeクラスがあるとしましょう:

 class Shape { get color() { return this._color; } set color(c) { this._color = parseColorAsRGB(c); this.markChanged(); //  canvas  } } 

この方法でコードを記述すると、 staticプロパティで以前と同じ問題に直面します。関数を定義(定義)するときに関数のプロトタイプを変更する構文的な方法はありません。 これはObject.setPrototypeOfで回避できますが、このアプローチは一般的に、作成時に関数プロトタイプを指定する機会がある場合よりも効率が悪く、JSエンジンの最適化の機会が少なくなります。

 class Circle { // .  } //    Object.setPrototypeOf(Circle.prototype, Shape.prototype); //    Object.setPrototypeOf(Circle, Shape); 

これはenoughいだけです。 クラスシンタックスを追加して、すべてのロジックを1か所にカプセル化して、後で「バインド」を行う必要がないようにしました。 Java、Ruby、およびその他のオブジェクト指向言語には、あるクラスを別のクラスのサブクラスとして宣言するための構文があるため、Javascriptもそうする必要があります。 extendsを使用して、次のように記述できます。

 class Circle extends Shape { // .  } 

prototypeプロパティを持つ有効なコンストラクターがある限り、 extends後に任意の式を配置できます。 たとえば、フィット:

nullを使用してObject.prototype継承しないようにすることができます。

スーパープロパティ


そのため、サブクラスを作成し、プロパティを継承できます。また、メソッドが親のメソッドとオーバーラップ( シャドウ )することもあります。 しかし、オーバーラップメカニズムをバイパスする場合はどうでしょうか。
特定の数による円のスケーリングを処理(処理)するCircleサブクラスを作成するとします。 これを達成するために、かなり不自然なクラスを書くことができます。

 class ScalableCircle extends Circle { get radius() { return this.scalingFactor * super.radius; } set radius() { throw new Error("ScalableCircle radius is constant." + "Set scaling factor instead."); } // ,   scalingFactor } 

radiusプロパティのゲッターはsuper.radius使用することにsuper.radius 。 この新しいsuperキーワードを使用すると、プロトタイプから始まるプロパティを検索できますが、 シャドウは無視されます。

メソッド定義(定義)構文を使用して宣言された関数では、 super[expr]プロパティ( super[expr] 、また機能します)へのアクセスを使用できます。 これらの関数は元のオブジェクトからプルできますが、アクセスはメソッドが初めて定義(定義)されたオブジェクトに関連付けられています。 これは、ローカル変数にメソッドをプルすることにより(ああ、このJavascript- およそTransl。 )、スーパーアクセスの動作を変更しないことを意味します。

 var obj = { toString() { return "MyObject: " + super.toString(); } } obj.toString(); // MyObject: [object Object] var a = obj.toString; a(); // MyObject: [object Object] 


翻訳者から:翻訳の利便性のため(そして、このトピックがコミュニティにどの程度関連しているかを確認するため)、記事をほぼ2つの等しい部分に分けます。

Source: https://habr.com/ru/post/J264813/


All Articles