高品質なJavaScriptコードの書き方

高品質なJavaScriptコードの書き方

少し前に「Human High Quality Male」という動画が流行りました。見たことがある学生も多いと思います。そこで今日は、「人間品質のコード」とは何かを皆さんにお伝えしたいと思います。ハハ、冗談です。

実際、私がシェアしているのは、日常生活で私がまとめたちょっとしたコツです。皆さんにインスピレーションと助けをもたらすことができれば幸いです。

高品質な JavaScript コードを書くにはどうすればよいでしょうか?次の記事を一緒に勉強しましょう

1. 読みやすいコード

まず、コードは自分自身やチームメンバーが読むために書かれており、適切な読み方は高品質なコードを書くための前提条件です。具体的な操作方法を4つまとめてご紹介します。

1. 統一コード形式

時々このように書いたり、時々別の方法で書いたりしないでください。一貫性のある書き方を心がけてください。次に例を示します。

//悪い
関数foo(x,y) {
  戻る {
    合計: x + y
  };
}
関数バー(m, n){
  ret = m*nとする
  ret を返します。
}
//良い
function foo(x, y) { // 適切なスペースで区切ります。通常、シンボルの前にはスペースは追加されませんが、シンボルの後にはスペースが追加されます。 return {
    sum: x + y, // 末尾のカンマは有効で、オブジェクトや配列への要素の追加や削除が簡単になります} // 終了セミコロンは省略できますが、リスクを回避する方法を知っておく必要があります}
関数バー(m, n) {
  ret = m * nとする
  リターン ret
}


コード形式を手動で決めるのは不便なので、prettier プラグイン (https://prettier.io/)などのツールを使用して形式を自動的に変換することができます。

2. マジックナンバーを削除する

magic numberプログラミングにおいてプログラムコード内に直接書き込まれる特定の数値です(「10」、「123」など)。プログラマーはプログラムを作成するときに値の意味を理解できますが、しばらくすると他のプログラマー、あるいはプログラマー自身にとっても値の目的を理解することは難しくなります。

//悪い
タイムアウトを設定します(blastOff、86400000)
document.onkeydown = 関数 (ev) {
  ev.keyCode === 13の場合{
    // やること
  }
}
//良い
定数 1 日のミリ秒 = 86400000
定数 ENTER_KEY = 13
setTimeout(blastOff、1日あたりミリ秒)
document.onkeydown = 関数 (ev) {
  ev.keyCode === ENTER_KEY の場合 {
    // やること
  }
}


もちろん、マジックストリングも上記と同様に扱われます。上記のコードでは定数に名前を付ける場合はアンダースコアを使用し、その他の変数や関数に名前を付ける場合はキャメルケースを使用することをお勧めします。

実は、 this の使用頻度を減らすことにも同じ原理が当てはまります。コードにthisが大量に含まれていれば、それが誰なのかわかりにくくなり、読むのに時間がかかってしまうことがよくあります。

//悪い
クラスFoo {
    関数foo(){
        この数値 = 100
        this.el.onclick = 関数 () {
            this.className = "アクティブ"
        }
    }
}
//良い
クラスFoo {
    関数foo(){
        コンテキスト = this
        コンテキスト.番号 = 100
        context.el.onclick = 関数 () {
            el = thisとする
            el.className = "アクティブ"
        }
    }
}

3. 単一機能原則

モジュール、クラス、関数のいずれを作成する場合でも、それぞれに単一の機能を持たせ、あまり多くの処理を行わないようにする必要があります。これにより、読みやすくなり、拡張も柔軟に行うことができます。

//悪い
関数コピー(obj, deep) {
  if (深い) {
    //ディープコピー} else {
    // 浅いコピー}
}
//良い
関数コピー(obj) {
  // 浅いコピー}
関数 deepCopy(obj) {
  // ディープコピー}

4. ネストレベル数を減らす

条件付きネスト、ループネスト、コールバックネストなど多段階ネストはコードの可読性に非常に不利なので、ネストレベルはできる限り減らすべきです。

ネストされた条件の問題を解決するには、通常、 guard clauseを使用して早期に戻り、ネストを減らすことができます。

//悪い
関数foo(){
  結果を出す
  if (isDead) {
    結果 = deadAmount()
  } それ以外 {
    if (isRet) {
      結果 = retAmount()
    } それ以外 {
      結果 = 通常の金額()
    }
  }
  結果を返す
}
//良い
関数foo(){
  if (isDead) {
    deadAmount() を返す
  }
  if (isRet) {
    retAmount() を返す
  }
  通常の金額を返す()
}

ガード文に加えて、短絡演算や条件演算子などを使用して条件文を書き換えることもできます。

//悪い
関数foo(){
    (正常かどうか)
        やること()
    }
    グレードを付ける
    if (isAdmin) {
        グレード = 1
    } それ以外 {
        グレード = 0
    }
}
//良い
関数foo(){
    isOk && todo() // 短絡演算 let grade = isAdmin ? 1 : 0 // 条件演算子 }

ネストされたコールバックの問題を解決するには、通常、「 async/await 」メソッドを使用して書き直すことができます。

//悪い
fs = require("fs") とします。
関数init() {
  fs.mkdir(ルート、(エラー) => {
    fs.mkdir(path.join(root, "public", "stylesheets"), (err) => {
      fs.writeFile() 関数は、
        path.join(ルート、"public"、"stylesheets"、"style.css")、
        "",
        関数 (エラー) {}
      )
    })
  })
}
初期化()
//良い
fs = require("fs").promises とします。
非同期関数init() {
  fs.mkdir(ルート) を待機します。
  fs.mkdir(path.join(root, "public", "stylesheets")) を待ちます。
  fs.writeFile(path.join(root, "public", "stylesheets", "style.css"), "") を待機します。
}
初期化()

上記で紹介した 4 つの提案に加えて、効果的な注釈、異なるタイプの比較の回避、ぎこちない文法の回避など、読書体験を改善できるポイントは他にもたくさんあります。

2. 高性能コード

ソフトウェア開発では、コードのパフォーマンスが製品のユーザーエクスペリエンスに直接影響するため、高品質のコードは高いパフォーマンスを備えている必要があります。具体的な操作方法を4つまとめてご紹介します。

ヒント: JavaScriptにかかる平均時間をテストするには、 console.time()メソッド、 JSBench.Meツール、 performanceツールなどを使用できます。

1. 最適化アルゴリズム

再帰は一般的なアルゴリズムです。以下は再帰を使用して実装された「階乗を求める」操作です。

//悪い
関数foo(n) {
  (n === 1)の場合{
    戻り値 1
  }
  n * foo(n - 1) を返す
}
foo(100) // 平均時間: 0.47ms
//良い
関数foo(n, 結果 = 1) {
  (n === 1)の場合{
    結果を返す
  }
  return foo(n - 1, n * result) // ここで末尾呼び出しの最適化}
foo(100) // 平均時間: 0.09ms


「末尾呼び出し」は、スタック フレームを再利用できるメモリ管理最適化メカニズムです。つまり、外部関数の戻り値が内部関数の戻り値になります。

2. 組み込みメソッドを使用する

多くの機能は、 JavaScriptの組み込みメソッドを使用して解決できます。組み込みメソッドの基礎となる実装は最適であることが多く、組み込みメソッドはインタープリタで事前に実行できるため、実行効率が非常に高くなります。

次の例は、オブジェクトの属性と値の複合配列形式を取得する方法を示しています

//悪い
データ = {
  ユーザー名: "leo",
  年齢: 20,
  性別:「男性」、
}
結果 = []
for (let attr in data) {
  結果.push([attr, data[attr]])
}
console.log(結果)
//良い
データ = {
  ユーザー名: "leo",
  年齢: 20,
  性別:「男性」、
}
結果 = Object.entries(データ)
console.log(結果)

3. スコープチェーンの検索を減らす

スコープ チェーンは、スコープ ルールの実装です。スコープ チェーンの実装により、そのスコープ内で変数にアクセスしたり、そのスコープ内で関数を呼び出すことが可能になります。スコープ チェーンは、一方向にのみアクセスできるリンク リストです。このリンク リストの各ノードは、実行コンテキストの変数オブジェクト (コード実行時のアクティブ オブジェクト) です。一方向リンク リストの先頭 (アクセスできる最初のノード) は常に、現在呼び出されて実行されている関数の変数オブジェクト (アクティブ オブジェクト) であり、末尾は常にグローバル アクティブ オブジェクトです。

概念が複雑すぎる場合は、下の図をご覧ください。

スコープチェーンは 3 (head: bar) -> 2 (foo) -> 1 (tail: global) となっているため、変数を参照する際は先頭で取得を完了するようにすると、パフォーマンスを節約できます。具体的な比較は以下のとおりです。

//悪い
関数foo(){
  $("li").click(function () { // グローバル検索を 1 回実行$("li").hide() // グローバル検索を再度実行$(this).show()
  })
}
//良い
関数foo(){
  let $li = $("li") // $liのスコープ検索レベルを下げます $li.click(function () {      
    $li.非表示()               
    $(これ).表示()
  })
}


スコープ チェーンの検索を削減することに加えて、同じ原則がオブジェクト プロパティの検索を削減する場合にも適用されます。

//悪い
関数isNull(引数) {
  Object.prototype.toString.call(arg) === "[オブジェクト Null]" を返します
}
関数isFunction(引数) {
  return Object.prototype.toString.call(arg) === "[オブジェクト関数]"
}
//良い
toString = Object.prototype.toString とします。
関数isNull(引数) {
  return toString.call(arg) === "[オブジェクト Null]"
}
関数isFunction(引数) {
  return toString.call(arg) === "[オブジェクト関数]"
}

4. コードの重複を避ける

プログラムを作成するときに、繰り返しコードが多くなることがありますが、繰り返し操作は避けるのが最善です。簡単な例を挙げて、ループを通じて条件を満たす最初の要素のインデックス位置を見つけてみましょう。

//悪い
インデックスを0にする
(i = 0, len = li.length; i < len; i++) の場合 {
    if (li[i].dataset.switch === "on") {
        インデックス = i
    }
}
//良い
インデックスを0にする
(i = 0, len = li.length; i < len; i++) の場合 {
    if (li[i].dataset.switch === "on") {
        インデックス = i
        break // 次のループは意味がなく、不要なコードを実行します}
}


「フィボナッチ数列」を計算する別のケースを見てみましょう。

//悪い
関数foo(n) {
  (n < 3) の場合 {
    戻り値 1
  }
  foo(n - 1) + foo(n - 2) を返す
}
foo(40) // 平均時間: 1043ms
//良い
キャッシュを {} にします
関数foo(n) {
  (n < 3) の場合 {
    戻り値 1
  }
  if (!cache[n]) {
    キャッシュ[n] = foo(n - 1) + foo(n - 2)
  }
  キャッシュを返す[n]
}
foo(40) // 平均時間: 0.16ms


ここでは、再帰実行の結果が配列にキャッシュされるため、次に繰り返されるコードはキャッシュ内のデータを直接読み取ることができ、パフォーマンスが大幅に向上します。

×印の部分はキャッシュされ、計算は繰り返されません。

上記で紹介した 4 つの提案に加えて、DOM 操作の削減、処理の調整、イベントの委任など、コードのパフォーマンスを向上できるポイントは他にも多数あります。

3. 堅牢なコード

いわゆる堅牢なコードとは、拡張可能、保守可能、テスト可能であるように記述されたコードです。具体的な操作方法を4つまとめてご紹介します。

1. 新しい構文を使用する

多くの新しい構文は以前の構文のバグを補うことができ、コードをより堅牢で将来性のあるものにします。

//悪い
変数a = 1
isNaN(NaN) // 真
isNaN(未定義) // 真
//良い
a = 1とする
Number.isNaN(NaN) // 真
Number.isNaN(undefined) // false


新しい構文により、以前の操作が簡素化され、コード構造がより明確になります。

//悪い
ユーザー = { 名前: "ジェームズ", 年齢: 36 }
関数foo(){
  arg = 引数
  名前 = ユーザー名
  年齢 = user.age とします
}
//良い
ユーザー = { 名前: "ジェームズ", 年齢: 36 }
function foo(...arg) { // 残りパラメータ let { name, age } = user // 分割代入 }

2. いつでも拡張可能

製品要件は常に新たな変更の影響を受けるため、ソフトウェアのスケーラビリティには高い要件が課せられます。そのため、堅牢なコードとはいつでも調整できるコードです。

//悪い
関数 foo(動物) {
  if (動物 === "犬" || 動物 === "猫") {
    // やること
  }
}
関数バー(名前、年齢) {}
バー("ジェームズ", 36)
//良い
関数 foo(動物) {
  const animals = ["dog", "cat", "hamster", "turtle"] // 拡張可能な一致する値 if (animals.includes(animal)) {
    // やること
  }
}
function bar(options) {} // 任意のパラメータを拡張できる bar({
  性別:「男性」、
  名前:「ジェームズ」、
  年齢: 36歳
})

3. 副作用を避ける

関数が値を取得して結果を返す以外の動作を実行する場合、副作用が発生します。副作用は必ずしも有害ではありませんが、プロジェクト内で無制限に発生すると、コードエラーが発生する可能性が非常に高くなります。

グローバル変数や可変オブジェクトを変更せず、パラメータとreturnを通じて要件を完了することをお勧めします。関数を純粋にすると、コードのテストも容易になります。

//悪い
フルーツ = 「アップルバナナ」
関数splitFruits() {
  フルーツ = fruit.split(" ")
}
関数 addItemToCart(カート, アイテム) {
  cart.push({ アイテム、データ: Date.now() })
}
//良い
フルーツ = 「アップルバナナ」
関数splitFruits(果物) {    
  戻り値: fruit.split(" ")
}
関数 addItemToCart(カート, アイテム) {
  [...cart, { item, data: Date.now() }] を返します
}

4. 論理的な懸念を統合する

プロジェクトが複雑すぎると、さまざまなロジックが混在することが多く、その後の拡張に非常に不利になり、コードの理解にも影響します。そのため、関連するロジックをまとめて抽出し、集中管理するようにしてください。 ReacthooksVue3Composition APIなど、これらはすべてこの考え方を採用しています。

//悪い
エクスポートデフォルト{
  名前: 'アプリ'、
  データ(){
    戻る {
      検索ホット: [],
      検索候補: [],
      検索履歴: [],
    },
    マウント() {
      // ホットなtodo
      
      // ToDo 履歴
    },
    メソッド: {
      ハンドル検索サジェスト(){
        // todo 提案
      },
      ハンドル検索履歴(){
        // ToDo 履歴
      }
    }
  }
}
//良い
エクスポートデフォルト{
  名前:「アプリ」、
  設定() {
    {searchHot} = useSearchHot() とします
    {searchSuggest、handleSearchSuggest} = useSearchSuggest() とします。
    {searchHistory、handleSearchHistory} = useSearchHistory() とします。
    戻る {
      検索ホット、
      検索提案、
      検索履歴、
      ハンドル検索提案、
      ハンドル検索履歴、
    }
  }
}
関数useSearchHot() {
  // ホットなtodo
}
関数useSearchSuggest() {
  // todo 提案
}
関数useSearchHistory() {
  // ToDo 履歴
}


上記で紹介した 4 つの提案に加えて、例外処理、単体テスト、JS の代わりに TS を使用するなど、コードの堅牢性を向上させるポイントは他にも多数あります。

最後に、高品質なJavaScriptコードの書き方をまとめてみましょう。


高品質な JavaScript コードの書き方に関するこの記事はこれで終わりです。高品質な JavaScript コードの書き方に関するより関連性の高いコンテンツについては、123WORDPRESS.COM の過去の記事を検索するか、以下の関連記事を引き続き参照してください。皆様の今後の 123WORDPRESS.COM へのご支援をお待ちしております。

以下もご興味があるかもしれません:
  • 高品質な JS コードを書く 12 の方法
  • 高品質な JavaScript コードを書くための基本
  • 高品質なJSコードの書き方(続き)
  • 高品質なJSコードの書き方
  • JavaScript学習ノートの徹底理解(I)高品質なコードの書き方
  • JavaScriptを深く理解するシリーズ(1)高品質なJavaScriptコードを書くための基礎

<<:  Docker で Jenkins サービスを構築する例

>>:  MySQL 5.7.31 64 ビット無料インストール版チュートリアル図

推薦する

after疑似要素を使用して中空の三角矢印とXアイコンを実装する例

フロントエンドのデザイン案では、「X」や「>」の形をした閉じるボタンや、他の 3 方向の白抜き...

インターフェース設計の10の一般的なルール

<br />これは私がずっと前に集めた記事です。皆さんの参考のために共有したいと思います...

Dockerコンテナを閉じずに終了する方法の詳細な説明

Docker コンテナに入った後、コンテナを終了すると、コンテナは Exited 状態に変わります。...

CSS変数var()の使い方を理解する必要があります

Web プロジェクトがどんどん大きくなると、CSS は天文学的な大きさと複雑さを増します。この問題を...

デザイン理論:テキスト表現とユーザビリティ

<br />テキストデザインでは、通常、テキストのレイアウト、つまりテキストをより美しく...

Linuxシステムはポート3306、8080などを外部に開放します。ファイアウォール設定の詳しい説明

多くの場合、Linux システムに Web サービス アプリケーション (Tomcat、Apache...

MySQL で行を列に変換したり、列を行に変換したりする詳細な例

mysql 行から列へ、列から行へ難しい文章ではないので、詳しく説明はしません。文章を読むときは、一...

ベスト HTML/CSS デザインおよび開発フレームワーク 15 選を紹介します

プロフェッショナルな Web デザインは複雑で時間がかかります。 HTML と CSS フレームワー...

Nginx ロードバランシング クラスタの実装

(1)実験環境youxi1 192.168.5.101 ロードバランサーyouxi2 192.168...

Linux C++ マルチスレッド同期の非常に詳細な説明

目次1. ミューテックス1. ミューテックスの初期化2. ミューテックスロックの関連特性と分類3. ...

CSS3 を使って本のページめくり効果を実現するサンプルコード

重要なポイント: 1. CSS3 3Dアニメーションをマスターする2. ページめくり後のページ内容の...

フォームの「Enter」、「Submit」、「Enter != Submit」を削除する方法

「Enter != Submit」問題を実装するには、通常、「ボタンの種類」と「入力ボックスの数」か...

CSSはコンテンツの高さが足りない場合にフッターを自動的に下部に固定します

UI カットのプロセスでは、ページはヘッダー、コンテンツ、フッターの 3 つの部分で構成されることが...

MySQLの基礎知識学習ノート

データベースを表示show databases;データベースを作成するDATABASE データベース...