11 の素晴らしい JavaScript コード リファクタリングのベスト プラクティスの概要

11 の素晴らしい JavaScript コード リファクタリングのベスト プラクティスの概要

パターンとリファクタリングの間には本質的な関係があります。ある観点から見ると、デザイン パターンの目的は、多くのリファクタリング アクティビティのターゲットを提供することです。

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 を使用した場合と同レベルです。
同様に、三項演算子によって節約されるコードの量は、コードの可読性と保守性の低下と比較するとごくわずかです。圧縮、キャッシュ、CDN の使用、異なるドメイン名の使用など、JS ファイルの読み込みを高速化する方法はたくさんあります。三項演算子を使用することで節約される文字数だけに注目するのは、体重 300 ポンドの人がフケのせいで体重が増えたと主張するようなものです。

条件分岐ロジックが単純かつ明確であれば、三項演算子を使用しても問題はありません。

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 を応援していただければ幸いです。

以下もご興味があるかもしれません:
  • PostgreSQL チュートリアル (XII): ロールと権限管理の概要
  • AngularJs でのロールベースのフロントエンド アクセス制御の実装
  • vue3 を使用したジグソーパズルゲームのリファクタリングの例
  • Python コードのリファクタリングの 6 つの実例
  • 高品質なEasy Languageコードを書くための注意点
  • Android コード品質管理の簡単な分析
  • 高品質な JS コードを書く 12 の方法
  • ソフトウェアエンジニアの自己啓発に関する簡単な議論

<<:  オブジェクト内のフィールドを削除する js メソッド

>>:  JS配列インデックス検出におけるデータ型の問題の詳細な説明

推薦する

Xampp サーバーで MySQL パスワードを変更する方法 (画像付き)

今日、PHP で作業しているときに、Xampp サーバーに付属の mysql データベースを使用する...

doctype のマークアップ検証

しかし最近、この方法を使用すると問題が発生することがわかりました。コードを参照してください。コードを...

複雑なSQLクエリを含むMySQLの一般的なSQL文の概要

1. 複雑なSQLクエリ1.1. 単一テーブルクエリ(1)指定の列を選択する[例] 全生徒の生徒ID...

優れた UI (ユーザー インターフェース) デザイナーになるための 20 の道標

はじめに: インターフェイス デザイナーの Joshua Porter が自身のブログでこの記事を公...

MySQLストレージエンジンについて学びましょう

目次序文1. MySQL メインストレージエンジン: 2. さまざまなストレージエンジンがテーブルを...

MySQL 同期遅延が発生したときに Seconds_Behind_Master が 0 のままになる理由

目次問題の説明原理分析問題分析拡大する総括する問題の説明ユーザーはプライマリ データベースに対して変...

Dockerを使用してSpringBootプロジェクトをデプロイする方法

Docker テクノロジの開発により、マイクロサービスの実装にさらに便利な環境が提供されます。Doc...

アバター変更機能を実装するJavaScript

この記事では、アバター変更機能を実装するためのJavaScriptの具体的なコードを参考までに共有し...

MySQLデータテーブルの基本操作:テーブル構造の操作、フィールド操作例の分析

この記事では、テーブル構造操作やフィールド操作など、MySQL データ テーブルの基本的な操作につい...

MySQL の 4 つのトランザクション分離レベルの詳細な説明

この実験のテスト環境: Windows 10+cmd+MySQL5.6.36+InnoDB 1. ト...

MySQL で group by を使用すると常にエラー 1055 が発生します (推奨)

MySQL で group by を使用すると常にエラー 1055 が発生するため、原因を確認する...

超詳細なMySQL使用仕様の共有

最近、データベース関連の操作が多くなり、会社の既存の仕様はあまり包括的ではありません。インターネット...

H5ウェイクアップアプリの実装方法と注意点のまとめ

目次序文APPメソッドにジャンプURLスキームメタタグユニバーサルリンクさまざまな使い方URLスキー...

Bash で山括弧を使用するその他の方法

序文この記事では、山括弧のその他の用途をさらに詳しく見ていきます。前回の記事では、山括弧 (<...

React構成サブルーティングの実装

1. コンポーネント First.js にはサブコンポーネントがあります。 './Admin...