クイズ 1
JavaScript には本当にクラスが必要ですか? まず、クラスを備えた他のオブジェクト指向言語 (Java など) の機能をいくつか見てみましょう。
スーパークラスとサブクラス スーパークラスとサブクラスは父と息子の問題を解決するためのものではなく、クラスの包含関係を解決するために使用されます。はい、「サブクラス」を表すために Sub を使用します「親クラス」を表すには、
Sub Sup
という違いがあります。例えば、通常、サブクラスを親クラスとして使用できますが、人を認識する場合、息子をその子と見なすことはできません。父親。
言い換えれば、親クラスとサブクラスは、クラス間の同じメソッドまたは属性の問題を解決するように設計されていません。
たとえば これを行うのが好きな人もいます:
画面上にいくつかの動く動物を作成するには、いくつかの動物のクラスが必要ですが、動く動物の一部は空を飛んでいる者もいるし、道路を歩いている者もいる。
それで、2 つの親クラスを作成します。1 つは Fly で、もう 1 つは Walk です。
クラスフライ{
フライ(){}
}
クラスウォーク{
ウォーク(){}
}
次に、ライオン (道を歩く他の動物を作成することもできます) は「歩く」カテゴリに属し、ワシ (空を飛ぶ他の動物を作成することもできます) は「飛ぶ」カテゴリに属します。
Class Lion extend Walk{
}
Class Eagle extend Fly{
}
最後に、Lion クラスと Eagle クラスのインスタンスをいくつか作成し、対応するメソッドを呼び出すと、画面上でいくつかのライオンとワシが動くようになります。
しかし、これは良いデザインではないかもしれません。たとえば、明日、上司が突然、空を飛んだり、道を歩いたり、時には飛んだりできる動物を飼いたいと考えています。 。
この場合、この解決策はまったく役に立ちません。
なぜこの設計は失敗したのでしょうか? 継承は条件付きであり、サブクラスは厳密に上向きに変換できる (親クラスになる) 必要があります。
上記の例:
ライオンは歩く動物 (Walk) に相当し、ワシは飛行する動物 (Fly) に相当すると想定されます。
サブクラスは厳密に上向きにキャストできるため、これは成功しているように見えますが、隠れた危険があります。
ある種のペガサスが介入したとき、ライオンは実際には単なる「歩く動物」であり、ワシは実際には単なる「空を飛ぶ動物」であることがわかりました。これは、動物が一生を通じて飛ぶか歩くことしかできないという意味ではありません。は飛ぶことも歩くこともできますが、自分の家を見つけることができません。
この例は、サブクラスと親クラスがクラス間で同じメソッドを持つという問題を解決するように設計されていないことをよく示しています:
一部の動物は歩くことができ、メソッド Walk を持つ必要がありますが、これはメソッドによって実行されるべきではありません。子クラスと親クラスの実装。
組み合わせ この問題は次のように解決できます:
クラス ライオン{
walker = new Walk();
return walker.walk();
}
クラス イーグル{
flyer = new Fly();
fly(){
returnFlyer.fly();
}
}
クラス ペガサス{
walker = new Walk();
flyer = new Fly();
return walker.walk();
fly(){
returnチラシ。
}
}
合成とは、単に新しいクラス内に元のクラスのオブジェクトを作成することです。したがって、組み合わせは、クラス間で同じメソッドを持つ問題を解決することです。この例では:
Walk は「歩く動物が持つべきメソッドのセット」とみなされ、同様に Fly は「歩く動物が持つべきメソッドのセット」とみなされます。したがって、Pegasus の場合、必要なのは次の組み合わせだけです。歩いて飛んでください。
継承の目的
継承はコードを再利用する唯一の方法ではありませんが、継承には利点があります。
サブクラスは親クラスに上位変換できます。
このようにして、すべてのサブクラスの違いを無視して、同じクラスとして動作させることができます。例:
メソッド fn(A)、fn(B) があります。これら 2 つのメソッドは実際には似ており、再利用したいと考えています。
次に、親クラス C を設定できます。ここで、A は C のサブクラス、B は C のサブクラスです。その後、fn(C) を A と B で再利用できます。
JavaScript に戻る しかし、JavaScript に戻ると、上記の例は当てはまらないことがわかりました。
JavaScript 自体は弱い型指定言語であるため、操作する前に操作するオブジェクトの型には注意を払いません (コンパイルする必要がないため)。成功するか、エラーが発生するだけです。
現時点では、継承は不要と思われます。そうすれば授業も必要なくなります。
私は 8 年間 JavaScript を書いてきましたが、uber 関数を使用する必要性を感じたことは一度もありません。スーパー アイデアは古典的なパターンではかなり重要ですが、プロトタイプおよび関数型パターンでは不要のようです。
—Douglas Crockford
私は 8 年間 JavaScript コードを書いてきましたが、スーパークラス関数を使用する必要性を感じたことはありません。スーパークラスの考え方は古典的なデザインパターンでは非常に重要ですが、プロトタイプや機能に基づくパターンでは必要ありません。今では、JavaScript でクラシック モードをサポートしようとした初期の試みは間違った決断だったと感じています。
安全な環境
もちろん、手動でタイプを決定し、パラメータのタイプを制御して、より安全な環境を提供することもできます。
たとえば、同じく弱い型指定のスクリプト言語である PHP は、強く型指定されたオブジェクト指向言語をシミュレートし、安全な環境をセットアップするために次のようにする必要があります:
class ShopProductWriter{
public function write( $shopProduct ){
if( ! ( $shopProductinstanceofCdProduct ) && ! ( $shopProductinstanceof BookProduct ) ){
die( "間違った型を入力してください" )
}
// 型が正しい場合は、何らかのコードを実行します
}
}
— —PHP オブジェクト、パターン、実践 第 3 版 . Matt Zandstra
しかし、これは非常に醜い解決策です。
古典的な継承構文のシュガー実装 しかし、古典的な継承は依然として多くの人に好まれている方法です。したがって、YUI、Prototype、Dojo、および MooTools はすべて、独自の実装ソリューションを提供します。
より一般的な解決策の中で、構文は大まかに次のとおりです:
var person = Class.extend({
init: function(isDancing){
this.dancing = isDancing;
}
}); Person.extend ({
init: function(){
this._super( true );
}
});
var n = new Dancer(); n.dancing ); //true
最も重要な実装は this._super の実装です。実際、extend 関数は渡されたオブジェクトを再構築してプロトタイプ オブジェクトに変換します。プロトタイプの新しいコンストラクター。
具体的な実装については参考資料 1 を参照してください。
ECMAScript 6 の古典的な継承構文シュガー
クラス ライブラリが独自の実装を実装することにより、多数の古典的な継承構文が生成されるため、ECMA 組織は満足していないようです。彼らは、より直観的なものを ECMAScript 6 の古典的な継承構文シュガーに追加しようとしています: class Animal {
constructor(name) {
this.name = name;
}
sayName() {
console.log(this.name);
}
}
class Dog extends Animal {
constructor(name) {
super(name);
bark() {
console.log( 「すごい!」);
}
}
概要 実際には、JavaScript では古典的な継承は必要ありません。
しかし、多くの人が古典的な継承モデルを好むため、関連する糖衣構文が ECMAScript 6 の新しいバージョンで提供されています。
しかし、中国では、フロントエンドでこの糖衣構文が広く使用されるのは遠い話のはずです...
クイズ 2 JavaScript 固有の継承についてはどうですか?
プロトタイプ継承 プロトタイプ継承は、古典的な継承におけるコレクション包含関係を解決しません。実際、プロトタイプ継承は従属関係を解決します。プロトタイプ ∈ Sup
子コンストラクター (サブタイプ) プロトタイプは、親コンストラクター (親タイプ) によって構築されるインスタンス オブジェクトです。プロトタイプは、実際にはサブタイプ インスタンス間で共有する必要があるものです:
function Being(){
this.living = true;
}
Being.prototype.walk = function(){
alert("私は歩いています"); 🎜>};
function Dancer(){
this.dancing = true;
Dancer.prototype = new Being();
Dancer.prototype.dance = function(){
alert ("私は踊っています");
var one = new Dancer()
one.dance();
借用、寄生、その他のテクノロジーを使用すると、さまざまな継承効果を生み出すことができますが、これらのテクノロジーは、プロトタイプ継承における属性とメソッドの一部の公開および非公開の問題を解決するだけです。紙面の都合上、これ以上は説明しませんので、興味があれば「Javascript Advanced Programming」の関連コンテンツを参照してください。
思考の質問
1. 記事冒頭のペガサスに関する質問を Javascript で記述する場合、どのように設計すればよいですか?たとえば、次の 2 つのタイプがあります:
コードをコピー
}
function Fly(){
this.fly = function(); {
/ /fly
}
}
;