Habrのすべての読者と作家への挨拶。
前回の記事では、マトリョーシカとの共同作業の基本について説明しました。 この中で、マトリョーシカを継承する方法と、今のところそれに基づいて小さなアプリケーションを構築する方法を説明したいと思います。
マトリョーシカは、カスタム関数
Class
      
      を使用して構築されたクラスの形式で配置されます。 これは、javascript.ruフォーラム ( ドックへのリンク )で書いた関数のわずかに変更されたバージョンです。
では、なぜクラスなのか? クラスは、プロトタイププログラミングのパラダイムと矛盾しない単なる単語です。 同じBackbone.jsのドキュメントを見ると、迷わずに「クラス」という単語も使用していることがわかります。 Javascriptにはクラスがなく、コンストラクターがあり、私はあなたに同意しますが、実際、この議論は理にかなっていますか? コンストラクターがクラスのように見え、クラスのように泳ぎ、クラスのように鳴る場合、これはおそらくクラスですか?
歌詞からアクションまで。 したがって、マトリョーシカはクラスとして作成されます。
 window.MK = window.Matreshka = Class({ ... });
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      クラス引数はプロトタイプコンストラクターであり、次のように定義できます。
 var MyClass = Class({ constructor: function() { ... } });
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
       ...その後、クラス関数から戻ります。 コンストラクターが定義されていない場合、空の関数になります。 
      1つのクラスを別のクラスから継承できます(この場合、
MyClass
      
      から継承されます)。
 var MyClass = Class({ 'extends': MK });
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      (
'extends'
      
      構文エラー(extendsは予約語です)を回避するためだけでなく、構文を強調するため
'extends'
      
      引用符が必要です。他のプロパティには引用符がありません。
.initMK
      
      継承中に重要なルールがあります:コンストラクターは常にする必要があり、その中で
.initMK
      
      メソッドを
.initMK
      
      必要があります。この場合、疑似
__id
      
      プロパティを初期化します:
__id
      
      (内部使用のインスタンス識別子)、
.__events
      
      オブジェクト(イベントオブジェクト)および。 (「特別な」プロパティの値、それらのアクセサ、および関連する要素を保存します)。 同じ規則がクラスにも当てはまります。これについては、
MK.Array
      
      および
MK.Object
      
      記事で説明します。
ログインフォームで動作する簡単なアプリケーションを作成しましょう。
(すぐに結果を見ることができます: jsbin.com/qohilowu/1/edit )
 <form class="login-form"> <input type="text" class="user-name" placeholder="Username"> <input type="password" class="password" placeholder="Password"> <label> <input type="checkbox" class="show-password"> Show Password </label> <input type="submit" value="Sign In" class="submit"> <label> <input type="checkbox" placeholder="Password" class="remember-me"> Remember me </label> </form>
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
       (ここでは、見た目を快適にするためだけの余分なブロックとクラスを削除しました。結果の例では、Bootstrapが使用されています)。 
      ログインとパスワードの2つのテキストフィールドがあります。 「パスワードを表示する」と「記憶する」の2つのチェックボックスがあります。 1つのボタンがあります:Enter。 ログインの長さが少なくとも4文字で、パスワードの長さが少なくとも5文字である場合、フォームの検証が完了したとします。
クラス
LoginForm
      
      作成します。 私の仕事では、1つの複雑な要素(フォーム、ウィジェットなど)に対して1つのクラスというルールに従います。 これで、
LoginForm
      
      が小さなアプリケーションの唯一のクラスになります。
 var LoginForm = Class( ... );
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      最初に行う必要があるのは、クラスがマトリョーシカを継承することを宣言することです。
 var LoginForm = Class({ 'extends': MK });
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      2番目-コンストラクターを宣言します。
 var LoginForm = Class({ 'extends': MK, constructor: function() { this.initMK(); //     ""  } });
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      次に、要素を対応するプロパティにバインドします。 個人的には、バインディングは常に
.bindings
      
      と呼ばれる別のメソッドに入れます。 これは好みの問題であり、問題なくコンストラクタ内の要素をバインドできます(ただし、
.initMK
      
      を呼び出した後にのみ)。
  ... bindings: function() { return this //      (.  (1)) // ,     ,  .innerHTML   ,       //  ,               .bindNode( this, '.login-form' ) //        (on, getValue, setValue), //       .bindNode({ userName: this.$( '.user-name' ), // ,      'keyup' password: this.$( '.password' ), // ,      'keyup' showPassword: this.$( '.show-password' ), // ,     'click' rememberMe: this.$( '.remember-me' ), // ,     'click' }) .bindNode( 'isValid', this.$( '.submit' ), { //   (.  (2)) setValue: function( v ) { $( this ).toggleClass( 'disabled', !v ); } }) ; }, ...
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      (1)文字列
".bindNode( this, '.login-form' )"
      
      どういう意味ですか? 現在のインスタンスが.bindNodeの最初の引数として渡される場合、マトリョーシカは実際に特別なプロパティ
"__this__"
      
      バインドします。 これは、レコード
 this.bindNode( this, '.login-form' );
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      これと同等:
 this.bindNode( '__this__', '.login-form' );
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      "__this__"
      
      特別な意味が必要なのはなぜですか?
.$
      
      メソッドを使用するために、アタッチする要素のコンテキストを設定できます(サンドボックス)。 このバインディングはオプションですが、望ましいです。 それと
.$
      
      メソッドを使用すると、異なるクラスで同じ要素をバインドするという理論上の矛盾を回避できます。
メソッドのドキュメント
.$
      
      : Http : //finom.github.io/matreshka/docs/Matreshka.html#$
(2)ここでこれをバインド
this.$( '.submit' )
      
      要素を
'isValid'
      
      プロパティ(以下で説明します)にこの方法で:
isValid == false
      
      場合、この要素に
'disabled'
      
      クラスを追加し、そうでない場合は削除しますこのクラス。
  著者からのコメント 
       この記事は、クラスを要素に設定するバインディングの略語を導入したバージョンのリリース前に書かれました。 バージョン0.1以降、Matryoshkaには静的メソッドMK.binders.classNameが含まれています 
      
        
        
        
      
ドキュメントリンク: finom.github.io/matreshka/docs/Matreshka.binders.html#className
        
        
        
      
このコード:
        
        
        
      
    
        
        
        
      
これで置き換えることができます:
        
        
        
      
    
        
        
        
      
    
ドキュメントリンク: finom.github.io/matreshka/docs/Matreshka.binders.html#className
このコード:
 .bindNode( 'isValid', this.$( '.submit' ), { setValue: function( v ) { $( this ).toggleClass( 'disabled', !v ); } })
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      これで置き換えることができます:
 .bindNode( 'isValid', this.$( '.submit' ), MK.binders.className( '!disabled' ) )
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
        コード設計について 
       メソッドの設計方法に注意してください。 マトリョーシカに基づいてアプリケーションを開発する過程で。 私はメソッドの連鎖 ( Method chaining )が大好きなので、メソッドが特定のものを返さない場合は、これを増やし
        
        
        
      
    
        
        
        
      
      
        
        
        
      
。
        
        
        
      
    
        
        
        
      
      
        
        
        
      
メソッドは、他の多くのメソッド(.set、.defineGetter ...)と同様にそれを行います。 さらに、ブロックチェーンブロックのルールを作成しました。
        
        
        
      
    
        
        
        
      
これにより、チェーンの始まりと終わりを簡単に見つけることができます。
        
        
        
      
    
this
      
      。
.bindNode
      
      メソッドは、他の多くのメソッド(.set、.defineGetter ...)と同様にそれを行います。 さらに、ブロックチェーンブロックのルールを作成しました。
( ) [].1() [].2() ... [].N() ( )
これにより、チェーンの始まりと終わりを簡単に見つけることができます。
.bindings
      
      を追加し
.bindings
      
      。
  ... constructor: function() { this .initMK() .bindings() //       .initMK() ; }, ...
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      次に、イベントを宣言し、それを別のメソッドに
.events
      
      ます。通常、このメソッドを
.events
      
      と呼び
.events
      
      。
  ... events: function() { this.boundAll().on( 'submit', function( evt ) { //     (.  (1)) this.login(); evt.preventDefault(); }.bind( this ) ); //     (.  (2)) return this //      "showPassword" (.  (3)) .on( 'change:showPassword', function() { this.bound( 'password' ).type = this.showPassword ? 'text' : 'password'; }, true ) //      "userName"  "password" (.  (4)) .on( 'change:userName change:password', function() { this.isValid = this.userName.length >= 4 && this.password.length >= 5; }, true ) ; }, ...
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      (1)フォーム要素(
this
      
      または
'__this__'
      
      バインドされて
this
      
      )、またはその
jQuery
      
      インスタンスを
jQuery.fn.on
      
      し、
'submit'
      
      イベントで
jQuery.fn.on
      
      メソッドを呼び出します。
.boundAll
      
      メソッドのドキュメント: http :
.boundAll
      
      (2) Function.prototype.bindメソッドを使用して、イベントハンドラーのコンテキストをバインドします。 これは、イベントハンドラー(要素)の標準の実行コンテキストをインスタンスに置き換えるために必要です。 IE8はこの方法をサポートしていませんが、常にes5-shimライブラリを使用することを強くお勧めします(次の記事で、これがどれほど優れているかを説明します)。
event.target
      
      、
event.delegatedTarget
      
      を使用して要素自体を取得するため、標準コンテキストを簡単に放棄します。
  将来のバージョンについて(実装済み)   Matryoshkaの将来のバージョンでは、ほとんどの場合、バインドされた要素にDOMイベントを追加する別の方法が表示されます。 バージョン0.1では、このメソッドはすでに実装されています。 
      
        
        
        
      
構文:
        
        
        
      
    
        
        
        
      
または
        
        
        
      
    
        
        
        
      
少し改善したいです。 そうする予定です:
        
        
        
      
    
        
        
        
      
    
      構文:
 this.boundAll().on( 'submit', function() { ... }.bind( this ) );
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      または
 this.boundAll( 'key' ).on( 'click', function() { ... }.bind( this ) );
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      少し改善したいです。 そうする予定です:
 this.on( 'submit::__this__', function() { ... }); this.on( 'click::key', function() { ... });
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      (3)ここで、コメントは1つの発言だけでそれ自体を表しています。
.on
      
      メソッドに渡される最後の引数に注意してください(
true
      
      )。 これはハンドラーのコンテキストではありません(型が
boolean
      
      )、この引数はハンドラーが宣言の直後、
.trigger
      
      を呼び出す必要がないことを示しています。
(4)
"userName"
      
      または
"password"
      
      プロパティを変更する場合、
'isValid'
      
      プロパティを
true
      
      または
false
      
      に設定し、ログインとパスワードの長さを確認します。
コンストラクターに追加します。
  ... constructor: function() { this .initMK() .bindings() .events() ; }, ...
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      ここで.loginメソッドを作成しますが、これまでのところ何も送信しません。
  ... login: function() { var data; if( this.isValid ) { data = { userName: this.userName, password: this.password, rememberMe: this.rememberMe }; alert( JSON.stringify( data ) ); } return this; } ...
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      そして、結果のクラスのインスタンスを作成します:
 var loginForm = new LoginForm();
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
        全コード 
        
        
        
      
    
       var LoginForm = Class({ 'extends': MK, rememberMe: true, constructor: function() { this .initMK() .bindings() .events() ; }, bindings: function() { return this .bindNode( this, '.login-form' ) .bindNode({ userName: this.$( '.user-name' ), password: this.$( '.password' ), showPassword: this.$( '.show-password' ), rememberMe: this.$( '.remember-me' ) }) .bindNode( 'isValid', this.$( '.submit' ), { setValue: function( v ) { $( this ).toggleClass( 'disabled', !v ); } }) ; }, events: function() { this.boundAll().on( 'submit', function( evt ) { this.login(); evt.preventDefault(); }.bind( this ) ); return this .on( 'change:showPassword', function() { this.bound( 'password' ).type = this.showPassword ? 'text' : 'password'; }, true ) .on( 'change:userName change:password', function() { this.isValid = this.userName.length >= 4 && this.password.length >= 5; }, true ) ; }, login: function() { var data; if( this.isValid ) { data = { userName: this.userName, password: this.password, rememberMe: this.rememberMe }; alert( JSON.stringify( data ) ); } return this; } }); var loginForm = new LoginForm();
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      結果: jsbin.com/qohilowu/1/edit
結論として
面白い効果に気付きました。 Javascriptでの4年間のプログラミング経験にもかかわらず、一定期間(数秒から数時間)他の人のコードやライブラリに遭遇するたびに、このコードで何が起こっているのか理解できません。 しかし、その後、より多くの行を読んだり、ドキュメントを調べたりした後、私には理解できないと思われたことが完全に透明になります。
マトリョーシカにより、私のコードは数桁安定になりました。 不注意によるエラーはほとんどなくなりました。 私のクライアントは、私がより少ない時間でより多くのことを始めたことを嬉しく思います。
次は?
確かに経験豊富なプログラマーは、
.login
      
      メソッドの不合理なデータ収集に気づいてい
.login
      
      。
 data = { userName: this.userName, password: this.password, rememberMe: this.rememberMe };
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      どこにアプリケーションの状態があり、データがどこにあるかを知っているツールを作成する方が良いと思いませんか? クラスにあるすべてのデータを送信することはあまり合理的ではありません。 データベースは、パスワードがユーザーに表示されるかどうかにまったく関心がありません。 サーバーは再度データをチェックして応答を送信するため、データベースは送信されたデータが有効かどうかを知る必要がありません。 この質問に対する解決策については、次の記事で説明します。次の記事では、
MK.Object
      
      クラスについて
MK.Object
      
      ます。
ご清聴ありがとうございました。 良いコーディング。