JavaScriptパターン疑似古典(疑似古典)

パターンでは、オブジェクトはコンストラクターで作成され、そのメソッドはプロトタイプで宣言されます。

このパターンは、Google Closure Libraryなどのフレームワークで使用されます。 ネイティブJavaScriptオブジェクトもこのパターンを使用します。

擬似クラス宣言


JavaScriptにはC、Java、PHPなどのような他の言語のようなクラスがないため、擬似クラスという用語が選択されましたが、このパターンはクラス定義に近いものです。

擬似クラスは、コンストラクター関数とメソッドで構成されます。

たとえば、 Animal擬似クラスは、1つのsitメソッドと2つのプロパティで構成されます。

 function Animal(name) { this.name = name } Animal.prototype = { canWalk: true, sit: function() { this.canWalk = false alert(this.name + ' sits down.') } } var animal = new Animal('Pet') // (1) alert(animal.canWalk) // true animal.sit() // (2) alert(animal.canWalk) // false 

  1. new Animal(name)が呼び出されると、オブジェクトはAnimal.prototypeへのAnimal.prototypeリンクをAnimal.prototype (図の右側を参照)。
  2. animal.sitメソッドは、インスタンス内のanimal.canWalkを変更するので、他の人ができるのに対して、私たちの動物はもはや歩くことができません。

画像
擬似クラススキーマ:

  1. デフォルトのメソッドとプロパティはプロトタイプで定義されています。
  2. prototypeメソッドはthis使用します。thisの値は呼び出しのコンテキストに依存するため、現在のオブジェクトを指します。 したがって、 animal.sit()では、 thisanimal指します。

継承


Animalから継承する新しいクラス、たとえばRabbit作成しましょう。

 function Rabbit(name) { this.name = name } Rabbit.prototype.jump = function() { this.canWalk = true alert(this.name + ' jumps!') } var rabbit = new Rabbit('John') 

ご覧のとおり、ウサギはAnimalと同じ構造をしています-メソッドはプロトタイプで定義されています。

Animalから継承するには、 Rabbit.prototype.__proto__ == Animal.prototypeを継承する必要があります。 メソッドがRabbit.prototypeで見つからない場合、親Animal.prototypeメソッドで検索するため、これは自然な要件Animal.prototype

このように、例えば:

画像

これを実装するには、まずRabbit.prototypeから継承した空のRabbit.prototypeオブジェクトを作成してから、メソッドを追加する必要があります。

 function Rabbit(name) { this.name = name } Rabbit.prototype = inherit(Animal.prototype) Rabbit.prototype.jump = function() { ... } 

inheritは、指定された__proto__空のオブジェクトを作成します。

 function inherit(proto) { function F() {} F.prototype = proto return new F } 

最後に起こったことは次のとおりです。

 // Animal function Animal(name) { this.name = name } // Animal methods Animal.prototype = { canWalk: true, sit: function() { this.canWalk = false alert(this.name + ' sits down.') } } // Rabbit function Rabbit(name) { this.name = name } // inherit Rabbit.prototype = inherit(Animal.prototype) // Rabbit methods Rabbit.prototype.jump = function() { this.canWalk = true alert(this.name + ' jumps!') } // Usage var rabbit = new Rabbit('Sniffer') rabbit.sit() // Sniffer sits. rabbit.jump() // Sniffer jumps! 

継承にnew Animalを使用しないでください


Rabbit.prototype = inherit(Animal.prototype)代わりに次のことを行うのは、よく知られていますが、正しい継承方法ではありません。

 // inherit from Animal Rabbit.prototype = new Animal() 

その結果、プロトタイプにnew Animalnew Animalれます。 new Animal Animal.prototype自然に継承するため、継承はAnimal.prototypeます。

...しかし、 new Animal()nameなしで呼び出すことができると言ったのは誰ですか? コンストラクターは厳密に引数を必要とし、引数なしで死ぬことができます。

実際、問題はこれよりも概念的です。 Animalは作成しません。 私たちは彼から継承したいだけです。

これが、 Rabbit.prototype = inherit(Animal.prototype)が望ましい理由Rabbit.prototype = inherit(Animal.prototype) 。 副作用のないきちんとした継承。

スーパークラスコンストラクターコール


スーパークラスコンストラクターが自動的に呼び出されるようになりました。 現在のオブジェクトに対してAnimal.apply()を使用して、ハンドルで呼び出すことができます。

 function Rabbit(name) { Animal.apply(this, arguments) } 

このコードは、現在のオブジェクトのコンテキストでAnimalコンストラクターを実行しnameインスタンスのname設定nameます。

メソッドの再定義(多態性)


親メソッドをオーバーライドするには、子プロトタイプで置き換えます:

 Rabbit.prototype.sit = function() { alert(this.name + ' sits in a rabbity way.') } 

rabbit.sit()が呼び出されると、 sitはチェーンrabbit -> Rabbit.prototype -> Animal.prototypeによって検索され、 Rabbit.prototypeに到達する前にAnimal.prototypeます。

もちろん、オブジェクト内で直接、別の方法で再定義できます。

 rabbit.sit = function() { alert('A special sit of this very rabbit ' + this.name) } 

オーバーライド後に親メソッドを呼び出す

メソッドをオーバーライドした後でも、親メソッドを呼び出す必要がある場合があります。 これは、親プロトタイプを直接使用する場合に可能です。

 Rabbit.prototype.sit = function() { alert('calling superclass sit:') Animal.prototype.sit.apply(this, arguments) } 

すべての親メソッドは、現在のオブジェクトがthisとして渡されるapply/callで呼び出されapply/callAnimal.prototype.sit()への単純な呼び出しは、 thisとしてAnimal.prototypeを使用しthis

砂糖:親への直接参照の削除

前の例では、親クラスを直接呼び出しました。 コンストラクターとして: Animal.apply... 、またはメソッド: Animal.prototype.sit.apply...

実際、これを行うべきではありません。 屈折処理中に、名前を変更したり、階層の中間クラスを追加したりできます。

通常、プログラミング言語では、 parent.method()super()などの特別なキーワードを使用して親メソッドを呼び出すことができます。

しかし、これはJavaScriptの場合ではありませんが、モデル化できます。

次の関数は継承を拡張し、直接参照せずに親とコンストラクタを設定します。

 function extend(Child, Parent) { Child.prototype = inherit(Parent.prototype) Child.prototype.constructor = Child Child.parent = Parent.prototype } 

だからここで使用できます:

 function Rabbit(name) { Rabbit.parent.constructor.apply(this, arguments) // super constructor } extend(Rabbit, Animal) Rabbit.prototype.run = function() { Rabbit.parent.run.apply(this, arguments) // parent method alert("fast") } 

その結果、 Animal名前を変更したり、中間のGrassEatingAnimalクラスを作成したりできますGrassEatingAnimalextend(...)のみが影響を受けます。

プライベートで安全な方法(カプセル化)


保護されたメソッドとプロパティは、命名規則によってサポートされています。 アンダースコア「_」で始まるメソッドは、外部から呼び出すべきではありません(実際には可能です)。

画像

プライベートメソッドはサポートされていません。

静的メソッドとプロパティ


静的メソッドとプロパティはコンストラクターで定義されます:

 function Animal() { Animal.count++ } Animal.count = 0 new Animal() new Animal() alert(Animal.count) // 2 

合計


スーパーメガOOPフレームワークは次のとおりです。

 function extend(Child, Parent) { Child.prototype = inherit(Parent.prototype) Child.prototype.constructor = Child Child.parent = Parent.prototype } function inherit(proto) { function F() {} F.prototype = proto return new F } 

使用法:

 // --------- the base object ------------ function Animal(name) { this.name = name } // methods Animal.prototype.run = function() { alert(this + " is running!") } Animal.prototype.toString = function() { return this.name } // --------- the child object ----------- function Rabbit(name) { Rabbit.parent.constructor.apply(this, arguments) } // inherit extend(Rabbit, Animal) // override Rabbit.prototype.run = function() { Rabbit.parent.run.apply(this) alert(this + " bounces high into the sky!") } var rabbit = new Rabbit('Jumper') rabbit.run() 

たとえば、あるオブジェクトから別のオブジェクトにプロパティをコピーする関数など、少しだけ砂糖をフレームワークに追加できます。

 mixin(Animal.prototype, { run: ..., toString: ...}) 

ただし、実際には、このOOPパターンを使用する必要はありません。 これを処理できるのは2つの関数のみです。

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


All Articles