高品質な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 ビット無料インストール版チュートリアル図

推薦する

JavaScript で動的な QML オブジェクトを作成する方法

1. オブジェクトを動的に作成するJavaScript からオブジェクトを動的に作成する方法は 2 ...

JavaScript プリミティブデータ型シンボルの詳細な説明

目次導入説明名前の競合私有財産要約する導入シンボル変数を作成する最も簡単な方法は、Symbol() ...

Windows および Linux での Redis のインストールとデーモン設定

# Windows および Linux 上の Redis のインストール デーモン構成Redis の...

Node.js パッケージ マネージャー npm の具体的な使用方法

目次目的npm init および package.json ファイルモジュールのインストールと管理モ...

MySQL はどのようにしてマスターとスレーブの同期を実現するのでしょうか?

マスタースレーブ同期 (マスタースレーブレプリケーションとも呼ばれる) は、マスタースレーブデータの...

CocosCreatorでスワイプした位置にテクスチャを表示する方法

目次1. プロジェクト要件2. 文書の内容3. プロジェクト例4. プロジェクトコード1. プロジェ...

nodejs + koa + typescript の統合と自動再起動に関する問題

目次バージョンノートプロジェクトを作成する依存関係をインストールするコンテンツの記入src/serv...

閲覧時に作成されたWebページの下部にある余分な空白スペースを削除する方法

Dreamweaver または FrontPage を使用して HTML Web ページを作成する場...

Linux でプロセスを効果的に管理するための 8 つのコマンド

序文プロセス管理の役割:サーバーの健全性状態を判定する: プロセスの状態 (メモリ、CPU 占有率な...

Linux で g++ を使用してプログラムをコンパイルする際の -I (大文字の i)、-L (大文字の l)、-l (小文字の l) の機能の詳細な説明

初心者の Linux ユーザーとして、私は単純なgcc/g++操作を何度も使用してきましたが、少し複...

HTMLページ埋め込み動画とJSコントロール切り替え動画例の詳しい説明

まず、ページにビデオを埋め込むための HTML コードは次のとおりです。コードをコピーコードは次のと...

ウェブサイトのアクセス速度を向上させるための徹底的な最適化に関するヒント

ウェブサイトのアクセス速度を向上させるための徹底的な最適化に関するヒント。ウェブサイトのアクセス速度...

nginxを使用してドメイン名ベースの仮想ホストを構成する

1. 仮想ホストとは何ですか?仮想ホストは、特殊なテクノロジーを使用して、実行中のサーバーを論理的に...

CSSをiPhoneのフルスクリーンに適応させる方法

1. メディアクエリ方式 /*iPhone X への適応*/ @media 画面のみ、(デバイス幅:...

iframe なしの div ネスト HTML

最近、宿題をしているときに、iframe を使用せずにページをネストする必要があったため、jquer...