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()



    では、 this



    animal



    指します。


継承



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 Animal



new 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/call



Animal.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



クラスを作成したりできますGrassEatingAnimal



extend(...)



のみが影響を受けます。



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



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



画像



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



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



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



 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つの関数のみです。



All Articles