簡単なJavaScriptクラスフレームワークの紹介_基礎知識

WBOY
リリース: 2016-05-16 15:52:57
オリジナル
1087 人が閲覧しました

現在進行中の JavaScript 本を執筆する際、私は JavaScript 継承システムにかなりの時間を費やし、その過程で古典的なクラス継承をシミュレートするためのさまざまなソリューションを研究しました。これらの技術的ソリューションの中で、base2 と Prototype の実装は私が最も賞賛するものです。

これらのソリューションから、イデオロギー的な意味を持つフレームワークを抽出する必要があります。そのフレームワークは、シンプルで再利用可能で、依存関係から独立している必要があります。使用例は次のとおりです:

var Person = Class. extend ( {
 init: function (isDancing ) {
  this. dancing = isDancing;
 },
 dance: function ( ) {
  return this. dancing;
 }
} );
var Ninja = Person.extend({
 init: function(){
  this._super( false );
 },
 dance: function(){
  // Call the inherited version of dance()
  return this._super();
 },
 swingSword: function(){
  return true;
 }
});
var p = new Person(true);
p.dance(); // => true
var n = new Ninja();
n.dance(); // => false
n.swingSword(); // => true
// Should all be true
p instanceof Person && p instanceof Class &&
n instanceof Ninja && n instanceof Person && n instanceof Class
ログイン後にコピー

注意すべき点がいくつかあります:

  • コンストラクターは単純である必要があります (init 関数を通じて実装されます)。
  • 新しく定義されたアナロジーは、既存のクラス
  • から継承する必要があります。
  • すべての「クラス」は祖先クラス Class を継承するため、新しいクラスを作成する場合、そのクラスは Class のサブクラスである必要があります。
  • 最も難しい点: 親クラスのオーバーライドされたメソッドは (構成コンテキストを通じて) アクセス可能でなければなりません。
  • 上の例では、 Person 親クラスの init() メソッドと dance() メソッドが this._super() を通じて呼び出されていることがわかります。

結果には非常に満足しています。クラス定義が構造化され、単一継承が維持され、スーパークラス メソッドを呼び出すことができました。

単純なクラスの作成と継承

以下はその実装です (読みやすく、コメントも付いています)。約 25 行です。ご提案を歓迎いたします。

/* Simple JavaScript Inheritance
 * By John Resig http://ejohn.org/
 * MIT Licensed.
 */
// Inspired by base2 and Prototype
( function ( ) {
 var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
 // The base Class implementation (does nothing)
 this.Class = function(){};
  
 // Create a new Class that inherits from this class
 Class.extend = function(prop) {
  var _super = this.prototype;
   
  // Instantiate a base class (but only create the instance,
  // don't run the init constructor)
  initializing = true;
  var prototype = new this();
  initializing = false;
   
  // Copy the properties over onto the new prototype
  for (var name in prop) {
   // Check if we're overwriting an existing function
   prototype[name] = typeof prop[name] == "function" &&
    typeof _super[name] == "function" && fnTest.test(prop[name]) ?
    (function(name, fn){
     return function() {
      var tmp = this._super;
       
      // Add a new ._super() method that is the same method
      // but on the super-class
      this._super = _super[name];
       
      // The method only need to be bound temporarily, so we
      // remove it when we're done executing
      var ret = fn.apply(this, arguments);    
      this._super = tmp;
       
      return ret;
     };
    })(name, prop[name]) :
    prop[name];
  }
   
  // The dummy class constructor
  function Class() {
   // All construction is actually done in the init method
   if ( !initializing && this.init )
    this.init.apply(this, arguments);
  }
   
  // Populate our constructed prototype object
  Class.prototype = prototype;
   
  // Enforce the constructor to be what we expect
  Class.prototype.constructor = Class;
  // And make this class extendable
  Class.extend = arguments.callee;
   
  return Class;
 };
})();
ログイン後にコピー

このうち、「初期化する/initを呼ばない」と「_superメソッドの作成」が最も難しいです。次に、誰もがその実装メカニズムをよりよく理解できるように、これについて簡単に紹介します。

初期化

関数プロトタイプの継承方法を説明するために、まず従来の実装プロセスを見てみましょう。これは、サブクラスのプロトタイプ属性が親クラスのインスタンスを指すようにするというものです。以下に示すように:


function Person ( ) { }
function Ninja ( ) { }
Ninja. prototype = new Person ( );
// Allows for instanceof to work:
(new Ninja()) instanceof Person
ログイン後にコピー

ただし、ここでの難しい点は、「instanceOf」の効果だけを取得したいだけであり、Person をインスタンス化し、そのコンストラクターを呼び出すという結果は必要ないことです。これを防ぐには、コード内で初期化する bool パラメータを設定します。この値は、親クラスがインスタンス化され、子クラスのプロトタイプ プロパティに設定された場合にのみ true になります。この処理の目的は、実際のインスタンス化中のコンストラクターの呼び出しとデザインの継承の違いを区別し、実際のインスタンス化中に init メソッドを呼び出すことです。

if ( !initializing )
 this.init.apply(this, arguments);
ログイン後にコピー
init 関数では、リソースを大量に消費するコード (サーバーへの接続、DOM 要素の作成など、誰も予測できません) が実行される可能性があるため、特に注意する価値があります。区別するために。

スーパーメソッド

継承を使用する場合、最も一般的な要件は、サブクラスがスーパークラスのオーバーライドされたメソッドにアクセスできることです。この実装では、最終的な解決策は、スーパークラス メソッドを指し、サブクラス メソッドでのみアクセスできる一時メソッド (._super) を提供することです。



var Person = Class. extend ( {
 init: function (isDancing ) {
  this. dancing = isDancing;
 }
} );
var Ninja = Person.extend({
 init: function(){
  this._super( false );
 }
});
var p = new Person(true);
p.dancing; // => true
var n = new Ninja();
n.dancing; // => false
ログイン後にコピー


この機能を実装するには、いくつかの手順が必要です。まず、extend を使用して、基本的な Person インスタンス (その構築プロセスは上で説明したクラス インスタンス) をリテラル オブジェクト (Person.extend() の関数パラメーター) とマージします。マージ プロセス中に、簡単なチェックが行われました。まず、マージされる属性が関数であるかどうかをチェックし、そうである場合は、上書きされるスーパークラス属性も関数であるかどうかをチェックします。両方のチェックが true の場合、このプロパティに対して _super メソッドを準備する必要があります。

追加されたスーパー メソッドをカプセル化するために、匿名クロージャ (関数オブジェクトを返す) がここで作成されることに注意してください。実行環境を維持する必要があるため、関数の実行後にリセットされるように古い this._super を保存する必要があります (同じ名前を付けたくない場合)。誤ってオブジェクト ポインタを失う) 予測できない問題。

次に、スーパー クラス内のオーバーライドされたメソッドのみを指す新しい _super メソッドを作成します。ありがたいことに、_super に変更を加えたりスコープを変更したりする必要はありません。関数の実行環境は関数呼び出しオブジェクト (ポインター this はスーパー クラスを指します) に応じて自動的に変更されるためです。

最後に、リテラル オブジェクトのメソッドを呼び出します。メソッドの実行中に This._super() が使用され、属性 _super が元の状態にリセットされ、関数が終了します。


同じ効果を達成する方法はたくさんあります (以前、super をそれ自体にバインドし、arguments.callee でアクセスするのを見てきました) が、この方法が使いやすさとシンプルさの特徴を最もよく反映していると感じます。

私が完成させた JavaScript プロトタイプに基づく多くの作品の中で、これは私が皆さんと共有するために公開した唯一のクラス継承実装計画です。私は、簡潔なコード (学習しやすく、継承しやすく、ダウンロードが少ない) を全員で議論できるようにする必要があると考えています。そのため、JavaScript クラスの構築と継承を学ぶ人にとって、この実装計画は良いスタートとなります。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート