勇哥

勇哥开发之路

I build things on web.

JavaScriptのベストプラクティス

本文翻译自 JETBRAINS Blog 的 JavaScript Best Practices,原作者是 David Watson

未来科技风格,机器人编程场景
JavaScript は間違いなく世界で最も広く使用されているプログラミング言語であり、私たちが依存する最大の技術の一つであるインターネットに大きな影響を与えています。この能力には大きな責任が伴い、JavaScript エコシステムは急速に進化しており、最新の JavaScript ベストプラクティスに追いつくことが非常に困難になっています。

この記事では、よりクリーンでメンテナンスしやすく、パフォーマンスの高いコードを書くための現代 JavaScript におけるいくつかの重要なベストプラクティスを紹介します。

プロジェクト定義の実践は他のすべての実践に勝る#

あなたがコードを書くプロジェクトには独自の厳格なルールがあるかもしれません。プロジェクトのルールは、どんなベストプラクティスの記事の提案よりも意味があります —— この記事を含めて!特定の方法を使用したい場合は、それをプロジェクトのルールやコードベースと同期させ、チームの全員が参加することを確認してください。

最新の JavaScript を使用する#

JavaScript は 1995 年 12 月 4 日に発明されました。それ以来、絶えず進化しています。オンラインでは、時代遅れの提案や慣行がたくさん見つかります。注意して、使用したい慣行が最新であることを確認してください。

また、最新の JavaScript 機能を使用する際には慎重に行動してください。少なくとも Ecma TC39 Stage 3 を経た新しい JavaScript 機能を使用することをお勧めします。

とはいえ、ここでは現在一般的な JavaScript ベストプラクティスをいくつか集めました:

変数の宣言#

多くの var 宣言を使用しているコードに出くわすかもしれません。これは意図的かもしれませんが、古いコードの場合、古い方法だからかもしれません。

提案: 変数を宣言する際には var の代わりに letconst を使用してください。

なぜこれが重要か: var はまだ使用可能ですが、letconst はブロックスコープを提供し、より予測可能で、var(関数スコープ)で変数を宣言する際に発生する可能性のある予期しないエラーを減少させます。

for (let j = 1; j < 5; j++) {
  console.log(j);
}
console.log(j);
// 'Uncaught ReferenceError: j is not defined' が表示されます

// var を使用してこれを行った場合:
for (var j = 1; j < 5; j++) {
  console.log(j);
}
// <-- 1 から 4 までの数字をログに記録します
console.log(j);
// ループの外にまだ存在するため、5 が表示されます

クラスの代わりに関数:プロトタイプ#

JavaScript における OOP に関する古いコードベースや記事では、クラスを模倣するための関数プロトタイプメソッドに出くわすかもしれません。例えば:

function Person(name) {
  this.name = name;
}
Person.prototype.getName = function () {
  return this.name;
}

const p = new Person('A');
console.log(p.getName()); // 'A'

提案: この方法はコンストラクタを使用してプロトタイプチェーンを制御するものです。しかし、この場合、クラスを使用する方がほぼ常に良いです。

class Person {
  constructor(name) {
    this.name = name;
  }
  getName() {
    return this.name;
  }
}

const p = new Person('A');
console.log(p.getName()); // 'A'

なぜ重要か: クラスを使用する主な理由は、より明確な構文を持っているからです。

プライベートクラスフィールド#

古い JavaScript コードでは、通常、アンダースコア (_) を慣習としてクラス内のプライベートプロパティやメソッドを示すために使用します。しかし、これは実際にはプライバシーを強制するものではなく、開発者に対して何かがプライベートであるべきであるという信号を送るだけです。

class Person {
  constructor(name) {
    this._name = name; // 慣習的にプライベートと見なされますが、実際にはプライベートではありません
  }

  getName() {
    return this._name;
  }
}

const p = new Person('A');
console.log(p.getName()); // 'A'
console.log(p._name); // 'A'(外部からもアクセス可能)

提案: クラス内でプライベートフィールドが本当に必要な場合、JavaScript は現在 # 構文を使用して真のプライベートフィールドを持っています。これは真のプライバシーを強制する公式の言語機能です。

アロー関数式#

アロー関数は、コールバック関数や匿名関数をより簡潔で読みやすくするために一般的に使用されます。特に mapfilterreduce などの高階関数を使用する際に便利です。

const numbers = [1, 2];

// アロー関数を使用
numbers.map(num => num * 2);

// 代わりに
numbers.map(function (num) {
  return num * 2;
});

提案: アロー関数は、特に関数本体が単一の式である場合に、より簡潔な構文を提供します。また、this コンテキストを自動的にバインドするため、クラスメソッドで特に便利です。なぜなら、this を失うのが容易だからです。

なぜ重要か: アロー関数は、ボイラープレートコードを削除することでコールバック関数やインライン式をより簡潔にし、可読性を向上させます。また、クラスやイベントハンドラーを使用する際に特に価値があり、周囲のレキシカルスコープに this を自動的にバインドします。これにより、非同期またはコールバック集中的なコードでの this に関連する一般的なエラーを回避できます。

Null 合併(??)#

JavaScript では、変数が undefined または null の場合、開発者はしばしば論理和(||)演算子を使用してデフォルト値を割り当てます。しかし、変数が 0false、または空文字列 ("") のような値を保持している場合、|| はそれらを偽の値と見なしてデフォルト値に置き換えるため、予期しない動作が発生する可能性があります。

例えば:

const value = 0;
const result = value || 10;
console.log(result); // 10(0 が有効な値の場合は予期しない)

提案: デフォルト値を解析する際には、論理和演算子 (||) の代わりに null 合併演算子 (??) を使用してください。これは undefined または null のみをチェックし、他の偽の値(0false"")はそのまま保持します。

const value = 0;
const result = value ?? 10;
console.log(result); // 0(期待される動作)

なぜ重要か: null または undefined がフォールバックを引き起こすべき場合、?? 演算子はデフォルト値を処理するためのより正確な方法を提供します。これは、|| を使用することで有効な偽の値が意図せず上書きされるのを防ぎます。null 合併を使用することで、より予測可能な動作を実現し、コードの明確さと信頼性を向上させます。

Optional Chaining(?.):#

深くネストされたオブジェクトや配列を扱う際、次のレベルにアクセスする前に各プロパティや配列要素が存在するかを確認する必要があります。オプショナルチェーンがない場合、これは冗長で繰り返しのコードを必要とします。

例えば:

const product = {};

// オプショナルチェーンなし
const tax = (product.price && product.price.tax) ?? undefined;

提案: オプショナルチェーン演算子(?.)は、プロパティやメソッドにアクセスする前に自動的にその存在を確認することで、このプロセスを簡素化します。チェーン内の任意の部分が null または undefined の場合、エラーをスローするのではなく、undefined を返します。

const product = {};

// オプショナルチェーンを使用
const tax = product?.price?.tax;

なぜ重要か: オプショナルチェーンは、ボイラープレートコードの量を減らし、深くネストされた構造を扱うのを容易にします。これは、空の値や未定義の値を優雅に処理することで、コードをよりクリーンでエラーが少なくし、複雑なデータを扱う際に特に役立ちます。

async/await#

古い JavaScript では、非同期操作を処理するためにコールバックやプロミスのチェーンに依存することが一般的で、すぐに複雑で読みづらいコードにつながることがありました。例えば、.then() を使用したプロミスチェーンは、特に複数の非同期操作がある場合、プロセスを追跡するのが難しくなることがあります:

function fetchData() {
  return fetch('https://api.example.com/data')
    .then(response => response.json())
    .then(data => {
      console.log(data);
    })
    .catch(error => {
      console.error(error);
    });
}

提案: asyncawait を使用して、非同期コードを通常の同期コードのように見せることができます。これにより、可読性が向上し、try...catch でのエラーハンドリングが容易になります。

async function fetchData() {
  try {
    const response = await fetch('https://api.example.com/data');
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error(error);
  }
}

なぜ重要か: async/await 構文は、.then().catch() のチェーンを排除し、非同期操作を簡素化します。これにより、コードがより読みやすく、メンテナンスしやすく、理解しやすくなります。特に複数の非同期呼び出しを扱う際に、try...catch でのエラーハンドリングもより直接的になり、より明確で予測可能なロジックを生み出します。

オブジェクトのキーと値とのインタラクション#

古い JavaScript コードでは、オブジェクトのキーと値とのインタラクションは通常、for...in または Object.keys() を使用して手動でループし、括弧記法またはドット記法で値にアクセスすることを含みます。これにより、コードが冗長で直感的でなくなる可能性があります。

const obj = { a: 1, b: 2, c: 3 };

// Object.keys() を使用した古いアプローチ
Object.keys(obj).forEach(key => {
  console.log(key, obj[key]);
});

提案: オブジェクトのキーと値を処理するために、Object.entries()Object.values()、および Object.keys() などの現代的な方法を使用してください。これらのメソッドはプロセスを簡素化し、有用な構造(配列など)を返し、コードをより簡潔で使いやすくします。

const obj = { a: 1, b: 2, c: 3 };

// キーと値のペアを反復処理するために Object.entries() を使用
Object.entries(obj).forEach(([key, value]) => {
  console.log(key, value);
});

// 値を直接操作するために Object.values() を使用
Object.values(obj).forEach(value => {
  console.log(value);
});

なぜ重要か: Object.entries()Object.values()、および Object.keys() などの現代的なオブジェクトメソッドを使用することで、より明確で読みやすいコードを生成できます。これらのメソッドは、オブジェクトを反復処理するために必要なボイラープレートの量を減らし、特に複雑または動的なデータ構造を扱う際にコードの明確さを向上させます。また、オブジェクトを他の形式(配列など)に変換することを容易にし、データ操作をより柔軟かつ効率的にします。

変数の配列タイプをチェックする#

かつて、開発者は変数が配列であるかどうかをチェックするためにさまざまな非直接的な方法を使用していました。これらの方法には、コンストラクタをチェックしたり、instanceof などの方法を使用したりすることが含まれますが、特に異なる実行コンテキスト(iframe など)を扱う場合、通常は信頼性がありません。

const arr = [1, 2, 3];
// 古いアプローチ
console.log(arr instanceof Array); // true、ただし異なるコンテキストでは常に信頼できるわけではありません

提案: 現代の Array.isArray() メソッドを使用してください。このメソッドは、変数が配列であるかどうかをチェックするためのシンプルで信頼できる方法を提供します。このメソッドは、異なる環境や実行コンテキストで一貫して機能します。

const arr = [1, 2, 3];
console.log(Array.isArray(arr)); // true

なぜ重要か: Array.isArray() は、配列をチェックするための明確で読みやすく信頼できる方法です。これは、instanceof のような冗長またはエラーを引き起こしやすい方法の必要性を排除し、複雑またはクロス環境のシナリオでも配列検出を正しく処理することを保証します。異なるタイプのデータ構造を使用する際に、エラーを減らし、動作をより予測可能にします。

Map#

初期の JavaScript では、開発者は通常、キーを値にマッピングするために通常のオブジェクトを使用していました。しかし、この方法には制限があり、特にキーが文字列やシンボルでない場合に問題が発生します。通常のオブジェクトは、キーとして文字列やシンボルのみを使用できるため、配列や他のオブジェクトのような非原始オブジェクトを値にマッピングする必要がある場合、面倒でエラーが発生しやすくなります。

const obj = {};
const key = { id: 1 };

// 非原始オブジェクトをキーとして使用しようとする
obj[key] = 'value';
console.log(obj); // オブジェクトは自動的にキーを文字列に変換します:'[object Object]: value'

提案: 非原始オブジェクトをマッピングする必要がある場合や、より強力なデータ構造が必要な場合は、Map を使用してください。通常のオブジェクトとは異なり、Map は任意のタイプの値(原始および非原始)をキーとして許可します。

const map = new Map();
const key = { id: 1 };

// Map で非原始オブジェクトをキーとして使用
map.set(key, 'value');
console.log(map.get(key)); // 'value'

なぜ重要か: Map は、任意のタイプのキー(原始キーまたは非原始キー)に値を関連付けるためのより柔軟で予測可能な方法です。これは、キーのタイプと順序を保持し、通常のオブジェクトがキーを文字列に変換するのとは異なります。これにより、特に複雑なデータを扱う場合や大きなコレクション内で迅速に検索する必要がある場合に、キーと値のペアを処理することがより強力かつ効率的になります。

隠された値のシンボル#

JavaScript では、オブジェクトは通常、キーと値のペアを格納するために使用されます。しかし、オブジェクトに「隠れた」またはユニークな値を追加する必要がある場合、他のプロパティとの名前の衝突のリスクを冒さずに、または外部コードにそれらを秘密にしたい場合、シンボルを使用することが非常に便利です。シンボルは、通常の列挙や偶発的なプロパティの検索ではアクセスできないユニークなキーを作成します。

const obj = { name: 'Alice' };

const hiddenKey = Symbol('hidden');
obj[hiddenKey] = 'Secret Value';

console.log(obj.name); // 'Alice'
console.log(obj[hiddenKey]); // 'Secret Value'

提案: オブジェクトに列挙不可能な隠しプロパティを追加したい場合は、Symbol を使用してください。典型的なオブジェクト操作(for...in ループや Object.keys() など)の間にシンボルにアクセスできないため、意図せずに露出すべきでない内部またはプライベートデータに非常に適しています。

const obj = { name: 'Alice' };

const hiddenKey = Symbol('hidden');
obj[hiddenKey] = 'Secret Value';

console.log(Object.keys(obj)); // ['name'](シンボルキーは表示されません)
console.log(Object.getOwnPropertySymbols(obj)); // [Symbol(hidden)](特に取得されない限りアクセスできません)

なぜ重要か: シンボルを使用すると、キーの衝突や内部の詳細をコードベースの他の部分に露出させることを心配することなく、オブジェクトにユニークで「隠れた」プロパティを安全に追加できます。これは、メタデータや内部状態を保存する必要があるライブラリやフレームワークで特に有用です。これにより、より良いカプセル化が保証され、偶発的な上書きや誤用のリスクが低減します。

追加のフォーマットライブラリを使用する前に Intl API を確認する#

かつて、開発者は通常、日付、数字、通貨などのフォーマットを異なる地域に適応させるためにサードパーティのライブラリに依存していました。これらのライブラリは強力な機能を提供しますが、プロジェクトに追加の負担をかけ、JavaScript にすでに組み込まれている機能を繰り返す可能性があります。

// 通貨フォーマットのためのライブラリを使用
const amount = 123456.78;
// formatLibrary.formatCurrency(amount, 'USD');

提案: 外部ライブラリを使用する前に、組み込みの ECMAScript 国際化 API(Intl)を使用することを検討してください。これは、地域設定に基づいて日付、数字、通貨などをフォーマットするための強力な現成功能を提供します。これにより、サードパーティのライブラリの追加コストなしに、ほとんどの国際化およびローカライズのニーズを満たすことができます。

const amount = 123456.78;

// 通貨フォーマットのために Intl.NumberFormat を使用
const formatter = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' });
console.log(formatter.format(amount)); // $123,456.78

重要性: Intl API は、国際化のためのネイティブで高度に最適化されたサポートを提供するため、単純なフォーマットニーズのために大きなライブラリをインポートする必要がありません。組み込み機能を使用することで、プロジェクトを軽量に保ち、依存関係を減らしながら、言語環境に基づくフォーマットソリューションを提供できます。これにより、パフォーマンスが向上し、サードパーティライブラリに関連するメンテナンスの負担が軽減されます。

一般的な慣行#

さて、ベストプラクティスであるべき一般的な慣行をいくつか見てみましょう。

可能な限り厳密な等価性(===)を使用する#

JavaScript で最も厄介で驚くべき動作の一つは、緩やかな等価演算子(==)から来ています。これは型強制を実行し、比較するオペランドを同じ型に変換しようとします。これは、比較するオペランドを同じ型に変換しようとするため、奇妙で予期しない結果を引き起こす可能性があります。これは、Brian Leroux のプレゼンテーションで有名な「WTFJS」ケースとして示されています:

console.log([] == ![]); // true を返します(驚きの意味は何ですか?!)

この場合、緩やかな等価演算子(==)は、双方を予期しない方法で変換し、直感的でない結果を引き起こします。

提案: 可能な限り厳密な等価性 (===) を使用し、緩やかな等価性 (==) を避けてください。厳密な等価性は型強制を実行せず、値と型を直接比較するため、より予測可能で信頼性の高い動作を生み出します。

console.log([] === ![]); // false を返します(期待通り)

以下は、違いを強調するためのより典型的な例です:

// 緩やかな等価性 (==) は型強制を実行します
console.log(0 == ''); // true

// 厳密な等価性 (===) は値と型の両方を比較します
console.log(0 === ''); // false(期待通り)

なぜ重要か: 厳密な等価性(===)を使用することで、JavaScript における型強制による予期しない動作を回避できます。これにより、比較がより予測可能になり、特に数字、文字列、ブール値などの異なるデータ型を扱う際に微細なエラーが発生するリスクが低減します。特別な理由がない限り、デフォルトで === を使用することは良い慣行です。

if 文の中の式を明確に処理する:#

JavaScript では、if 文は計算された式の結果を暗黙的に「真」または「偽」に変換します。これは、0""(空文字列)、nullundefined、および false などの値が偽の値と見なされる一方で、ほとんどの他の値([]{} のような値さえも)が真の値と見なされることを意味します。注意しないと、この暗黙の変換が時折予期しない結果を引き起こすことがあります。

例えば:

const value = 0;

if (value) {
  console.log('これは実行されません。なぜなら 0 は偽だからです。');
}

提案: if 文の条件を明示的に示すことは良い慣行です。特に、使用している値が真 / 偽の評価で異常な動作を示す可能性がある場合は、これによりコードがより予測可能で理解しやすくなります。

例えば、暗黙の型強制に依存するのではなく:

const value = 0;

// 暗黙のチェック(いくつかの値に対して予期しない動作を示す可能性があります)
if (value) {
  console.log('これは実行されません');
}

明示的な条件を使用できます:

// 期待する型または値を明示的にチェックします
if (value !== 0) {
  console.log('これは value が 0 でない場合にのみ実行されます。');
}

または、null または undefined をチェックする場合:

const name = null;

if (name != null) { // null または undefined を明示的にチェックします
  console.log('名前が定義されています');
} else {
  console.log('名前は null または undefined です');
}

なぜ重要か: if 文の条件を明示的に定義することで、JavaScript の自動型強制による予期しない動作の可能性を減らすことができます。これにより、コードがより明確になり、0falsenull、または "" のような曖昧な値を使用する際のエラーを防ぐのに役立ちます。特に複雑なロジックの中で、チェックしたい条件を明示的に示すことは良い慣行です。

内蔵の Number を使用して敏感な計算を行わない#

JavaScript の内蔵 Number 型は、IEEE 754 標準に基づく浮動小数点数です。これはほとんどの用途には効果的ですが、特に 10 進数の算術において驚くべき不正確さを引き起こす可能性があります。これは JavaScript に特有の問題ではありませんが、敏感なデータ(例えば財務計算)を扱う場合、深刻な問題を引き起こす可能性があります。

例えば、次の有名な浮動小数点の問題に遭遇するかもしれません:

console.log(0.1 + 0.2); // 0.30000000000000004

提案: 精度が重要な場合(例えば財務計算など)、標準の Number 型を使用して算術演算を行うのは避けてください。代わりに、decimal.jsbig.js のような、浮動小数点エラーなしで正確な 10 進数計算を処理するために設計された専用のライブラリを使用してください。

以下は、それが decimal.js などのライブラリとどのように機能するかを示しています:

const Decimal = require('decimal.js');

const result = new Decimal(0.1).plus(0.2);
console.log(result.toString()); // '0.3'

これらのライブラリは計算の正確性を保証し、丸め誤差が結果に影響を与えないようにするため、金銭などの敏感なタスクを扱うのに理想的です。

なぜ重要か: 財務データなどを扱う際に、計算の不正確さは深刻な問題を引き起こす可能性があります。微小な差異でさえ重大な影響を及ぼすことがあります。JavaScript の浮動小数点数学は予期しない結果を生む可能性があるため、現在のところ、decimal.jsbig.js などのライブラリに依存して精度を確保するのが最善です。これらのライブラリを使用することで、一般的な落とし穴を避け、計算が正確で信頼できることを保証できます。

JSON と大きな整数の使用に注意#

JavaScript には非常に大きな数字を処理する際の制限があります。JavaScript の最大安全整数は 9007199254740991Number.MAX_SAFE_INTEGER とも呼ばれます)です。この値を超える数字は精度を失い、誤った結果を引き起こす可能性があります。JavaScript 以外の API やシステムを使用する場合、特に大きな数字(例えばデータベースの ID フィールド)は、JavaScript の安全範囲を超えることが容易です。

例えば、大量の数字を含む JSON を解析する際:

console.log(
  JSON.parse('{"id": 9007199254740999}')
); 
// 出力: { id: 9007199254741000 }(精度が失われます)

提案: JSON データ内の大量の数字を扱う際には、JSON.parse()reviver パラメータを使用して、この精度の問題を回避できます。これにより、特定の値(例えば id フィールド)を手動で処理し、安全な形式(例えば文字列)で保存できます。

console.log(
  JSON.parse(
    '{"id": 9007199254740999}',
    (key, value, ctx) => {
      if (key === 'id') {
        return ctx.source; // 元の値を文字列として保持します
      }
      return value;
    }
  )
); 
// 出力: { id: '9007199254740999' }

BigInt の使用: JavaScript は BigInt を導入して、Number.MAX_SAFE_INTEGER を超える数字を安全に処理します。ただし、BigInt は JSON に直接シリアライズできません。BigInt を含むオブジェクトを文字列化しようとすると、TypeError が発生します:

const data = { id: 9007199254740999n };

try {
  JSON.stringify(data);
} catch (e) {
  console.log(e.message); // 'BigInt をシリアライズする方法がわかりません'
}

この問題を解決するために、JSON.stringify() の replacer パラメータを使用して BigInt 値を文字列に変換します:

const data = { id: 9007199254740999n };

console.log(
  JSON.stringify(data, (key, value) => {
    if (typeof value === 'bigint') {
      return value.toString() + 'n'; // BigInt を示すために 'n' を追加します
    }
    return value;
  })
);
// 出力: {"id":"9007199254740999n"}

⚠️重要な注意事項: これらの技術を使用して JSON 内の大きな整数を処理する場合、アプリケーションのクライアントとサーバーがデータをシリアライズおよびデシリアライズする方法について合意していることを確認してください。例えば、サーバーが特定の形式の文字列または BigInt として id を送信する場合、クライアントはデシリアライズ中にその形式を処理する準備が必要です。

なぜ重要か: 外部システムからの大量の数字を扱う際に、JavaScript の数字精度制限は深刻なエラーを引き起こす可能性があります。BigInt などの技術や JSON.parse() および JSON.stringify()reviver/replacer パラメータを使用することで、大きな整数を正しく処理し、データの損傷を回避できます。これは、特に大きな ID や金融取引を扱う際に重要です。

JSDoc を使用してコードの読み手と編集者を助ける#

JavaScript を使用する際、関数やオブジェクトの署名には通常文書が欠けており、他の開発者(将来の自分自身を含む)がパラメータやオブジェクトが何を含むか、または関数をどのように使用するかを理解するのが難しくなります。適切な文書がない場合、特にオブジェクト構造が不明確な場合、コードは曖昧さを生む可能性があります。

例えば:

const printFullUserName = user =>
  // ユーザーは `middleName` または `surName` を持っていますか?
  `${user.firstName} ${user.lastName}`;

この場合、文書がないため、ユーザーオブジェクトがどのプロパティを持つべきかがすぐには明らかではありません。usermiddleName を含んでいますか? lastName の代わりに surName を使用すべきですか?

提案: JSDoc を使用することで、オブジェクト、関数パラメータ、および戻り値の期待される構造を定義できます。これにより、コードの読み手が機能を理解しやすくなり、コードエディタがより良いオートコンプリート、型チェック、およびツールチップを提供するのに役立ちます。

以下は、JSDoc を使用して前の例を改善する方法を示しています:

/**
 * @typedef {Object} User
 * @property {string} firstName
 * @property {string} [middleName]  // オプションのプロパティ
 * @property {string} lastName
 */


/**
 * ユーザーのフルネームを印刷します。
 * @param {User} user - 名前の詳細を含むユーザーオブジェクト。
 * @return {string} - ユーザーのフルネーム。
 */
const printFullUserName = user =>
  `${user.firstName} ${user.middleName ? user.middleName + ' ' : ''}${user.lastName}`;

重要性: JSDoc は、関数やオブジェクトに必要な値の型を明確に示すことで、コードの可読性とメンテナンス性を向上させます。また、Visual Studio Code や WebStorm などの多くのエディタや IDE でオートコンプリートや型チェックを有効にすることで、開発者体験を向上させます。これにより、エラーが発生する可能性が減り、新しい開発者がコードに参加しやすくなります。

JSDoc を使用することで、エディタはヒントやオブジェクトプロパティのオートコンプリートを提供し、開発者が関数を誤用したり、誤ったパラメータ型を提供したりした場合に警告を発することができます。これにより、コードがより理解しやすく、堅牢になります。

テストを使用する#

コードベースが成長するにつれて、新しい変更が重要な機能を壊さないことを手動で確認するのは非常に時間がかかり、エラーが発生しやすくなります。自動テストは、コードが期待どおりに動作することを保証し、安心して変更を加えることを可能にします。

JavaScript エコシステムには、多くのテストフレームワークが利用可能ですが、Node.js バージョン 20 以降、テストを開始して実行するために外部フレームワークは必要ありません。Node.js には、組み込みの安定したテストランナーが含まれています。

以下は、Node.js の組み込みテストランナーを使用した簡単な例です:

import { test } from 'node:test';
import { equal } from 'node:assert';


// テストするための簡単な関数
const sum = (a, b) => a + b;

// sum 関数のテストを書く
test('sum', () => {
  equal(sum(1, 1), 2); // 1 + 1 が 2 に等しい場合は true を返すべきです
});

このテストを実行するには、次のコマンドを使用します:

node --test

この組み込みのソリューションは、Node.js 環境でテストを書くプロセスを簡素化します。Jest や Mocha などの他のツールを設定またはインストールする必要はなくなりましたが、これらのオプションは大規模なプロジェクトには依然として非常に便利です。

ブラウザでの E2E テスト: ブラウザでのエンドツーエンド (E2E) テストには、Playwright が優れたツールであり、ブラウザ内のインタラクションを自動化およびテストするのが簡単です。Playwright を使用すると、ユーザーフローをテストし、複数のブラウザ(Chrome、Firefox、Safari など)でのインタラクションをシミュレートし、アプリケーションがユーザーの視点から期待どおりに動作することを確認できます。

他の環境: Bun や Deno などの代替 JavaScript ランタイムも、Node.js と同様の組み込みテストランナーを提供しているため、追加の設定なしで簡単にテストを作成および実行できます。

なぜ重要か: 長期的には、テストを書くことで時間を節約でき、早期にエラーを発見し、手動テストの必要性を減らすことができます。また、新機能やリファクタリングが回帰を引き起こさないことを確認できます。実際、Node.js、Bun、Deno などの最新のランタイムには組み込みのテストランナーが含まれているため、最小限の設定でテストをすぐに書き始めることができます。Playwright などのテストツールは、アプリケーションが実際のブラウザ環境でシームレスに動作することを保証し、重要なユーザーインタラクションに追加の保証を提供します。

最後の考え#

これが学ぶべき多くの内容に見えるかもしれませんが、以前に考慮していなかった分野に深く入り込み、JavaScript プロジェクトで実装したいと思うことを願っています。再度強調しますが、この内容をブックマークしておき、必要に応じていつでも参照してください。JavaScript の慣行は常に変化し進化しています。フレームワークも同様です。最新のツールやベストプラクティスに追いつくことは、コードを継続的に改善し最適化するために重要ですが、これを行うのは難しい場合があります。最新の ECMAScript バージョンの動向に注目することをお勧めします。これは、最新の JavaScript コードで一般的に採用されている新しい慣行を指し示すことがよくあります。TC39 は、最新の ECMAScript バージョンに関する提案を行っており、これらの提案をフォローすることもできます。

これらの現代的な JavaScript ベストプラクティスを採用することで、単に機能するコードを書くのではなく、より明確で効率的、かつメンテナンスしやすいソリューションを作成できます。async/await などの新しい構文を使用することや、浮動小数点の落とし穴を避けること、強力な Intl API を活用することなど、これらの慣行は、最新の状態を保ち、コードベースに自信を持つのに役立ちます。JavaScript エコシステムが進化し続ける中で、今こそベストプラクティスを採用する時間を取ることで、将来のトラブルを回避し、長期的な成功に備えることができます。

今日はここまでです!この記事があなたに役立つことを願っています —— コメントや質問、議論、提案を歓迎します。楽しいコーディングを!

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。