パターンとリファクタリングの間には本質的な関係があります。ある観点から見ると、デザイン パターンの目的は、多くのリファクタリング アクティビティのターゲットを提供することです。 1. 関数の抽出JavaScript 開発では、関数を扱うことにほとんどの時間を費やすため、これらの関数に適切な名前が付けられ、関数本体に含まれるロジックが明確であることが望まれます。関数が長すぎて、読みやすくするために複数のコメントが必要な場合は、これらの関数をリファクタリングする必要があります。
たとえば、ユーザー情報を取得する関数では、ユーザー情報に関連するログも印刷する必要があるため、ログを印刷するためのステートメントを別の関数にカプセル化できます。 var getUserInfo = 関数(){ ajax( 'http:// xxx.com/userInfo', 関数( データ ){ console.log( 'userId: ' + data.userId ); console.log( 'ユーザー名: ' + data.ユーザー名 ); console.log( 'ニックネーム: ' + data.ニックネーム ); }); }; に: var getUserInfo = 関数(){ ajax( 'http:// xxx.com/userInfo', 関数( データ ){ printDetails(データ); }); }; var printDetails = 関数(データ){ console.log( 'userId: ' + data.userId ); console.log( 'ユーザー名: ' + data.ユーザー名 ); console.log( 'ニックネーム: ' + data.ニックネーム ); }; 2. 重複した条件付きスニペットを結合する関数の本体に条件分岐ステートメントがいくつかあり、これらの条件分岐ステートメント内に重複するコードが散在している場合は、重複するコードをマージして削除する必要があります。ジャンプするページ番号を表すパラメータ currPage を受け取るページング関数 paging があるとします。ジャンプする前に、currPage に小さすぎる数値や大きすぎる数値が渡されないように、次の疑似コードに示すように、手動で値を修正する必要があります。 var paging = function(currPage){ (現在のページが0以下の場合) 現在のページ = 0; jump( currPage ); // ジャンプ }else if ( currPage >= totalPage ){ currPage = 合計ページ数; jump( currPage ); // ジャンプ }else{ jump( currPage ); // ジャンプ } }; ご覧のとおり、ジャンプを担当するコード jump(currPage) は各条件分岐に表示されるため、このコードは分離できます。 var paging = function(currPage){ (現在のページが0以下の場合) 現在のページ = 0; }それ以外の場合 (currPage >= totalPage){ currPage = 合計ページ数; } jump( currPage ); // ジャンプ関数を分離する }; 3. 条件分岐文を関数に抽出するプログラミングにおいて、複雑な条件分岐ステートメントは、プログラムの読みやすさや理解しやすさを難しくする重要な理由であり、簡単に大きな機能につながる可能性があります。製品の価格を計算する getPrice 関数を記述する必要があるとします。製品の計算には、夏であればすべての製品が 20% 割引で販売されるという 1 つのルールのみがあります。コードは次のとおりです。 var getPrice = 関数(価格) { var date = 新しい Date(); if ( date.getMonth() >= 6 && date.getMonth() <= 9 ){ // 夏の戻り価格 * 0.8; } 返品価格; }; 次のコードを確認してください: date.getMonth() >= 6 かつ date.getMonth() <= 9 の場合{ // ... } このコードの意味は非常に単純で、夏(7月から10月)かどうかを判断することです。このコードは短いですが、コードで表現されている意図とコード自体の間にはまだ距離があります。コードを読む人は、コードが伝える意図を理解するためにより多くの労力を費やす必要があります。実際、このコードは別の関数に洗練することができ、コードの意味をより正確に表現できるだけでなく、関数名自体がコメントとして機能することもできます。コードは次のとおりです。 var isSummer = 関数(){ var date = 新しい Date(); date.getMonth() >= 6 かつ date.getMonth() <= 9 を返します。 }; var getPrice = 関数(価格) { if ( isSummer() ){ // 夏の戻り価格 * 0.8; } 返品価格; }; 4. ループを適切に使用する関数本体では、一部のコードが実際に何らかの繰り返し作業を担当している場合、ループを合理的に使用することで、同じ機能を完了できるだけでなく、コードの量も削減できます。以下は、XHR オブジェクトを作成するコードです。例を簡略化するために、バージョン 9 未満の IE ブラウザーのみを考慮します。コードは次のとおりです。 var createXHR = 関数(){ var xhr; 試す{ xhr = 新しい ActiveXObject( 'MSXML2.XMLHttp.6.0' ); }キャッチ(e){ 試す{ xhr = 新しい ActiveXObject( 'MSXML2.XMLHttp.3.0' ); }キャッチ(e){ xhr = 新しい ActiveXObject( 'MSXML2.XMLHttp' ); } } xhr を返します。 }; var xhr = createXHR(); 「 これで、ループを柔軟に使用して、上記のコードと同じ効果を得ることができます。 ```js var createXHR = 関数(){ var バージョン = [ 'MSXML2.XMLHttp.6.0ddd', 'MSXML2.XMLHttp.3.0', 'MSXML2.XMLHttp' ]; ( var i = 0, version; version = バージョン[ i++ ]; ){ 試す{ 新しい ActiveXObject( バージョン ) を返します。 }キャッチ(e){ } } }; var xhr = createXHR(); 5. 条件分岐をネストするのではなく、関数を早めに終了する多くのプログラマーは、「各関数には 1 つのエントリと 1 つの終了しか設定できません」という考えを持っています。現代のプログラミング言語では、関数のエントリは 1 つだけに制限されています。しかし、「関数には出口が 1 つしかない」ということについては、さまざまな意見があることがよくあります。 次の疑似コードは、「関数には終了が 1 つだけある」というルールに準拠した典型的なコードです。 var del = 関数(obj){ var ret; if ( !obj.isReadOnly ){ // 読み取り専用でない場合にのみ削除できます if ( obj.isFolder ){ // フォルダーの場合 ret = deleteFolder( obj ); }else if ( obj.isFile ){ // ファイルの場合 ret = deleteFile( obj ); } } ret を返します。 }; ネストされた条件分岐ステートメントは、コード管理者にとって間違いなく悪夢です。コードを読む人にとって、ネストされた if および else ステートメントは、フラットな if および else ステートメントよりも読みにくく、理解しにくいものです。外側の if 分岐の左括弧と右括弧が 500 メートル離れていることもあります。 「リファクタリング」の言葉で言えば、ネストされた条件分岐は、「各関数には 1 つの出口しか存在しない」と固く信じているプログラマーによって記述されることが多いです。しかし実際には、関数の残りの部分に興味がない場合は、すぐに終了する必要があります。読者に役に立たない else セクションを見るように指示すると、プログラムの理解を妨げるだけです。 したがって、いくつかの条件分岐を選択し、これらの条件分岐に入った直後に関数を終了させることができます。これを行うには、一般的なトリックがあります。つまり、ネストされた if ブランチに直面したときに、外側の if 式を逆にすることができます。再構築された del 関数は次のようになります。 var del = 関数(obj){ if ( obj.isReadOnly ){ // if式を反転する return; } フォルダーの場合 deleteFolder(obj) を返します。 } if ( obj.isFile ) { deleteFile(obj) を返します。 } }; 6. 長いパラメータリストの代わりにオブジェクトパラメータを渡す関数が複数のパラメータを受け取る場合があり、パラメータの数が増えるほど、関数を理解して使用することが難しくなります。この関数を使用する人は、まずすべてのパラメータの意味を理解する必要があります。使用時には、パラメータを忘れたり、2 つのパラメータの位置が逆になったりしないように注意する必要があります。 3 番目と 4 番目のパラメータの間に新しいパラメータを追加する場合は、多くのコード変更が必要になります。コードは次のようになります。 var setUserInfo = function( id, name, address, sex, mobile, qq ){ コンソールにログ出力します。 console.log( '名前= ' +名前 ); console.log( 'アドレス= ' + アドレス ); console.log( '性別= ' + 性別 ); console.log( 'mobile= ' + mobile ); コンソールログ('qq=' + qq); }; setUserInfo( 1314, 'sven', 'shenzhen', '男性', '137********', 377876679 ); このとき、すべてのパラメータをオブジェクトに入れて、そのオブジェクトを setUserInfo 関数に渡すことができます。setUserInfo 関数に必要なデータは、オブジェクト自体から取得できます。パラメータの数と順序を気にする必要はなくなり、パラメータに対応するキー値が変更されていないことを確認するだけです。 var setUserInfo = 関数(obj){ console.log( 'id= ' + obj.id ); console.log( 'name= ' + obj.name ); console.log( 'address= ' + obj.address ); console.log( '性別= ' + obj.性別 ); console.log( 'mobile= ' + obj.mobile ); console.log( 'qq= ' + obj.qq ); }; ユーザー情報の設定({ id: 1314, 名前: 'スヴェン', 住所: '深セン', 性別: '男性'、 モバイル: '137********', お問い合わせ: 377876679 }); 7. パラメータの数を最小限に抑える関数を呼び出すときに複数のパラメータを渡す必要がある場合、この関数は困難になります。これらのパラメータが何を表しているかを理解し、順番に慎重に関数に渡す必要があります。パラメータを渡さずに関数を使用できる場合、この種類の関数は非常に人気があります。実際の開発では、関数にパラメータを渡すことは避けられませんが、関数が受け取るパラメータの数を減らすように努めるべきです。ここに非常に簡単な例があります。描画関数 draw がありますが、これは現在正方形のみを描画できます。この関数は図形の幅、高さ、正方形の 3 つのパラメータを受け取ります。 var draw = function( 幅、高さ、正方形 ){}; しかし実際には、正方形の面積は幅と高さから計算できるので、描画関数からパラメータ square を削除することができます。 var draw = function(幅, 高さ){ var square = 幅 * 高さ; }; この描画関数が将来的に円の描画をサポートするようになると仮定すると、パラメータ width と height を半径 radius に置き換える必要があります。ただし、図形の面積の正方形はクライアントによって渡されるべきではなく、渡されたパラメータに特定のルールを追加することで描画関数内で計算される必要があります。この時点で、戦略パターンを使用して、描画関数を複数のグラフィックスの描画をサポートする関数にすることができます。 8. 三項演算子の使用を避ける一部のプログラマーは、従来の if 文と else 文の代わりに、三項演算子を大規模に使用することを好みます。その理由は、三項演算子はパフォーマンスが高く、必要なコードが少ないからです。しかし、これらの理由はどちらも維持するのが困難です。 三項演算子が if や else よりも実際に効率的であると仮定したとしても、この違いは完全に無視できます。実際の開発では、コードが 100 万回ループされたとしても、三項演算子を使用した場合の時間オーバーヘッドは、if や else を使用した場合と同レベルです。 条件分岐ロジックが単純かつ明確であれば、三項演算子を使用しても問題はありません。 var global = typeof window !== "undefined" ? window : this; しかし、次のコードに示すように、条件分岐ロジックが非常に複雑な場合は、if と else を段階的に記述するのが最善の選択です。 if 文と else 文には多くの利点があります。まず、比較的読みやすいです。次に、三項演算子の周りのコードを変更するよりも簡単に変更できます。 もし ( !aup || !bup ) { === doc ? -1 を返します: b === ドキュメント ? 1 : オープ?-1: バップ?1: 入力をソートしますか? ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : 0; } 9. チェーン呼び出しを適切に使用するjQuery をよく使用するプログラマーは、メソッド呼び出しの連鎖にかなり慣れています。JavaScript では、次のコードに示すように、メソッド呼び出しが完了した後にオブジェクト自体を返す、つまりメソッド呼び出しの連鎖を実装するのは簡単です。 var ユーザー = 関数(){ this.id = null; this.name = null; }; User.prototype.setId = 関数(id){ id は、 これを返します。 }; User.prototype.setName = function(名前){ this.name = 名前; これを返します。 }; console.log( 新しいユーザー().setId( 1314 ).setName( 'sven' ) ); または: var ユーザー = { id: null、 名前: null、 setId: 関数(id) { id は、 これを返します。 }, setName: 関数(名前){ this.name = 名前; これを返します。 } }; console.log( User.setId( 1314 ).setName( 'sven' ) ); チェーン呼び出し方式を使用すると、読みにくくなることはそれほどなく、確かに一部の文字と中間変数を節約できますが、節約される文字数もごくわずかです。チェーン呼び出しの欠点は、デバッグ中に非常に不便なことです。チェーンでエラーが発生したことがわかっている場合は、まずチェーンを逆アセンブルして、デバッグ ログやブレークポイントを追加し、エラーの場所を特定する必要があります。 チェーンの構造が比較的安定しており、後で簡単に変更できない場合は、チェーン呼び出しを使用しても問題はありません。ただし、チェーンが変更されやすく、デバッグやメンテナンスが困難になる場合は、通常の呼び出し形式を使用することをお勧めします。 var user = 新しい User(); ユーザーIDの設定(1314) ユーザー.setName( 'sven' ); 10. 大きなクラスを分割するストリートファイターの HTML5 版の最初のバージョンでは、ゲーム キャラクターの作成を担当する Spirit クラスが非常に大きくなっています。キャラクターのスプライトの作成を担当するだけでなく、キャラクターの攻撃、防御、その他のアクション メソッドも含まれています。コードは次のとおりです。 var Spirit = 関数(名前){ this.name = 名前; }; Spirit.prototype.attack = function( type ){ // 攻撃 if ( type === 'waveBoxing' ){ console.log( this.name + ': 波動拳を使う' ); }それ以外の場合(type === 'whirlKick'){ console.log( this.name + ': 旋風キックを使用する' ); } }; var spirit = new Spirit( 'RYU' ); spirit.attack( 'waveBoxing' ); // 出力: RYU: 波動拳を使用する spirit.attack( 'whirlKick' ); // 出力: RYU: WhirlKick を使用する その後、Spirit.prototype.attack メソッドの実装が大きすぎることが判明し、実際にはそれを別のクラスとして持つことが絶対に必要でした。オブジェクト指向設計では、適切な数の小さなオブジェクトに動作を分散することが推奨されます。 var Attack = function( スピリット ){ this.spirit = 精神; }; Attack.prototype.start = function(type){ this.list[ type ].call( this ) を返します。 }; 攻撃プロトタイプリスト = { ウェーブボクシング: 関数(){ console.log( this.spirit.name + ': 波動拳を使う' ); }, whirlKick: 関数(){ console.log( this.spirit.name + ': 旋風キックを使用する' ); } }; Spirit クラスははるかにシンプルになり、さまざまな攻撃メソッドは含まれなくなりました。代わりに、攻撃アクションを Attack クラスのオブジェクトに委任します。このコードも、Strategy パターンのアプリケーションです。 var Spirit = 関数(名前){ this.name = 名前; this.attackObj = 新しいAttack( this ); }; Spirit.prototype.attack = function( type ){ // 攻撃 this.attackObj.start( type ); }; var spirit = new Spirit( 'RYU' ); spirit.attack( 'waveBoxing' ); // 出力: RYU: 波動拳を使う spirit.attack( 'whirlKick' ); // 出力: RYU: 旋風を使う 11. 複数のループを終了するには return を使用する関数本体に二重ループ文があり、特定の重大な条件に達したときに内側のループで判断し、外側のループを終了する必要があるとします。ほとんどの場合、制御フラグ変数を導入します。 var func = 関数(){ var フラグ = false; ( var i = 0; i < 10; i++ ) { ( var j = 0; j < 10; j++ ) { ( i * j > 30 )の場合{ フラグ = true; 壊す; } } if ( フラグ === true ) { 壊す; } } }; 2 番目のアプローチは、ループ フラグを設定することです。 var func = 関数(){ 外側ループ: ( var i = 0; i < 10; i++ ) { 内部ループ: ( var j = 0; j < 10; j++ ) { ( i * j > 30 )の場合{ 外側のループを中断します。 } } } }; これらのアプローチはどちらも間違いなく混乱を招きますが、より簡単な方法は、ループを終了する必要があるときにメソッド全体を終了させることです。 var func = 関数(){ ( var i = 0; i < 10; i++ ) { ( var j = 0; j < 10; j++ ) { ( i * j > 30 )の場合{ 戻る; } } } }; もちろん、 return を使用してメソッドを直接終了すると問題が発生します。ループ後に実行するコードがある場合はどうなるでしょうか?メソッド全体を早期に終了すると、このコードは実行される機会がなくなります。 var func = 関数(){ ( var i = 0; i < 10; i++ ) { ( var j = 0; j < 10; j++ ) { ( i * j > 30 )の場合{ 戻る; } } } console.log( i ); // このコードは実行される可能性はありません}; この問題を解決するには、ループの後の return の後にコードを配置します。コードが多数ある場合は、別の関数に絞り込む必要があります。 var print = 関数(i){ コンソールにログ出力します。 }; var func = 関数(){ ( var i = 0; i < 10; i++ ) { ( var j = 0; j < 10; j++ ) { ( i * j > 30 )の場合{ print(i) を返します。 } } } }; 関数(); これで、11 の素晴らしい JavaScript コード リファクタリングのベスト プラクティスに関するこの記事は終了です。JavaScript コード リファクタリングに関する関連コンテンツをさらにご覧になりたい場合は、123WORDPRESS.COM で以前の記事を検索するか、以下の関連記事を引き続き参照してください。今後も 123WORDPRESS.COM を応援していただければ幸いです。 以下もご興味があるかもしれません:
|
<<: オブジェクト内のフィールドを削除する js メソッド
>>: JS配列インデックス検出におけるデータ型の問題の詳細な説明
今日、PHP で作業しているときに、Xampp サーバーに付属の mysql データベースを使用する...
しかし最近、この方法を使用すると問題が発生することがわかりました。コードを参照してください。コードを...
1. 複雑なSQLクエリ1.1. 単一テーブルクエリ(1)指定の列を選択する[例] 全生徒の生徒ID...
はじめに: インターフェイス デザイナーの Joshua Porter が自身のブログでこの記事を公...
目次序文1. MySQL メインストレージエンジン: 2. さまざまなストレージエンジンがテーブルを...
目次問題の説明原理分析問題分析拡大する総括する問題の説明ユーザーはプライマリ データベースに対して変...
Docker テクノロジの開発により、マイクロサービスの実装にさらに便利な環境が提供されます。Doc...
この記事では、アバター変更機能を実装するためのJavaScriptの具体的なコードを参考までに共有し...
この記事では、テーブル構造操作やフィールド操作など、MySQL データ テーブルの基本的な操作につい...
この実験のテスト環境: Windows 10+cmd+MySQL5.6.36+InnoDB 1. ト...
MySQL で group by を使用すると常にエラー 1055 が発生するため、原因を確認する...
最近、データベース関連の操作が多くなり、会社の既存の仕様はあまり包括的ではありません。インターネット...
目次序文APPメソッドにジャンプURLスキームメタタグユニバーサルリンクさまざまな使い方URLスキー...
序文この記事では、山括弧のその他の用途をさらに詳しく見ていきます。前回の記事では、山括弧 (<...
1. コンポーネント First.js にはサブコンポーネントがあります。 './Admin...