modern-javascript-patterns
ES6+の非同期処理(async/await)、分割代入、スプレッド演算子、アロー関数、Promise、モジュール、イテレーター、ジェネレーター、関数型プログラミングパターンを駆使して、クリーンで効率的なJavaScriptコードを実現します。レガシーコードのリファクタリング、モダンなパターンの実装、JavaScriptアプリケーションの最適化に活用してください。
description の原文を見る
Master ES6+ features including async/await, destructuring, spread operators, arrow functions, promises, modules, iterators, generators, and functional programming patterns for writing clean, efficient JavaScript code. Use when refactoring legacy code, implementing modern patterns, or optimizing JavaScript applications.
SKILL.md 本文
Modern JavaScript Patterns
ES6+ の機能、関数型プログラミングパターン、クリーンで保守性が高く、高性能なコードを書くためのベストプラクティスに関する包括的なガイド。
このスキルを使用する場合
- レガシー JavaScript を最新の構文にリファクタリングする
- 関数型プログラミングパターンを実装する
- JavaScript のパフォーマンスを最適化する
- 保守性が高く読みやすいコードを書く
- 非同期操作を扱う
- 最新の Web アプリケーションを構築する
- コールバックから Promises/async-await に移行する
- データ変換パイプラインを実装する
ES6+ コア機能
1. アロー関数
構文と使用例:
// 従来の関数
function add(a, b) {
return a + b;
}
// アロー関数
const add = (a, b) => a + b;
// 単一パラメータ (括弧はオプション)
const double = (x) => x * 2;
// パラメータなし
const getRandom = () => Math.random();
// 複数のステートメント (波括弧が必要)
const processUser = (user) => {
const normalized = user.name.toLowerCase();
return { ...user, name: normalized };
};
// オブジェクトを返す (括弧で囲む)
const createUser = (name, age) => ({ name, age });
Lexical の 'this' バインディング:
class Counter {
constructor() {
this.count = 0;
}
// アロー関数は 'this' コンテキストを保持
increment = () => {
this.count++;
};
// 従来の関数はコールバック内で 'this' を失う
incrementTraditional() {
setTimeout(function () {
this.count++; // 'this' は undefined
}, 1000);
}
// アロー関数は 'this' を保持
incrementArrow() {
setTimeout(() => {
this.count++; // 'this' は Counter インスタンスを参照
}, 1000);
}
}
2. 分割代入
オブジェクト分割代入:
const user = {
id: 1,
name: "John Doe",
email: "john@example.com",
address: {
city: "New York",
country: "USA",
},
};
// 基本的な分割代入
const { name, email } = user;
// 変数名を変更
const { name: userName, email: userEmail } = user;
// デフォルト値
const { age = 25 } = user;
// ネストされた分割代入
const {
address: { city, country },
} = user;
// rest 演算子
const { id, ...userWithoutId } = user;
// 関数パラメータ
function greet({ name, age = 18 }) {
console.log(`Hello ${name}, you are ${age}`);
}
greet(user);
配列分割代入:
const numbers = [1, 2, 3, 4, 5];
// 基本的な分割代入
const [first, second] = numbers;
// 要素をスキップ
const [, , third] = numbers;
// rest 演算子
const [head, ...tail] = numbers;
// 変数を入れ替え
let a = 1,
b = 2;
[a, b] = [b, a];
// 関数の戻り値
function getCoordinates() {
return [10, 20];
}
const [x, y] = getCoordinates();
// デフォルト値
const [one, two, three = 0] = [1, 2];
3. スプレッド演算子と Rest パラメータ
スプレッド演算子:
// 配列の展開
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = [...arr1, ...arr2];
// オブジェクトの展開
const defaults = { theme: "dark", lang: "en" };
const userPrefs = { theme: "light" };
const settings = { ...defaults, ...userPrefs };
// 関数の引数
const numbers = [1, 2, 3];
Math.max(...numbers);
// 配列/オブジェクトのコピー (浅いコピー)
const copy = [...arr1];
const objCopy = { ...user };
// 不変にアイテムを追加
const newArr = [...arr1, 4, 5];
const newObj = { ...user, age: 30 };
Rest パラメータ:
// 関数の引数を収集
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}
sum(1, 2, 3, 4, 5);
// 通常のパラメータと組み合わせ
function greet(greeting, ...names) {
return `${greeting} ${names.join(", ")}`;
}
greet("Hello", "John", "Jane", "Bob");
// オブジェクト rest
const { id, ...userData } = user;
// 配列 rest
const [first, ...rest] = [1, 2, 3, 4, 5];
4. テンプレートリテラル
// 基本的な使用
const name = "John";
const greeting = `Hello, ${name}!`;
// 複数行文字列
const html = `
<div>
<h1>${title}</h1>
<p>${content}</p>
</div>
`;
// 式の評価
const price = 19.99;
const total = `Total: $${(price * 1.2).toFixed(2)}`;
// タグ付きテンプレートリテラル
function highlight(strings, ...values) {
return strings.reduce((result, str, i) => {
const value = values[i] || "";
return result + str + `<mark>${value}</mark>`;
}, "");
}
const name = "John";
const age = 30;
const html = highlight`Name: ${name}, Age: ${age}`;
// 出力: "Name: <mark>John</mark>, Age: <mark>30</mark>"
5. 拡張オブジェクトリテラル
const name = "John";
const age = 30;
// プロパティ名の短縮
const user = { name, age };
// メソッド名の短縮
const calculator = {
add(a, b) {
return a + b;
},
subtract(a, b) {
return a - b;
},
};
// 計算されたプロパティ名
const field = "email";
const user = {
name: "John",
[field]: "john@example.com",
[`get${field.charAt(0).toUpperCase()}${field.slice(1)}`]() {
return this[field];
},
};
// 動的プロパティ作成
const createUser = (name, ...props) => {
return props.reduce(
(user, [key, value]) => ({
...user,
[key]: value,
}),
{ name },
);
};
const user = createUser("John", ["age", 30], ["email", "john@example.com"]);
非同期パターン
1. Promise
Promise の作成と使用:
// Promise を作成
const fetchUser = (id) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (id > 0) {
resolve({ id, name: "John" });
} else {
reject(new Error("Invalid ID"));
}
}, 1000);
});
};
// Promise を使用
fetchUser(1)
.then((user) => console.log(user))
.catch((error) => console.error(error))
.finally(() => console.log("Done"));
// Promise チェーン
fetchUser(1)
.then((user) => fetchUserPosts(user.id))
.then((posts) => processPosts(posts))
.then((result) => console.log(result))
.catch((error) => console.error(error));
Promise コンビネータ:
// Promise.all - すべての Promise を待つ
const promises = [fetchUser(1), fetchUser(2), fetchUser(3)];
Promise.all(promises)
.then((users) => console.log(users))
.catch((error) => console.error("At least one failed:", error));
// Promise.allSettled - すべてを待つ (結果に関わらず)
Promise.allSettled(promises).then((results) => {
results.forEach((result) => {
if (result.status === "fulfilled") {
console.log("Success:", result.value);
} else {
console.log("Error:", result.reason);
}
});
});
// Promise.race - 最初に完了したものを返す
Promise.race(promises)
.then((winner) => console.log("First:", winner))
.catch((error) => console.error(error));
// Promise.any - 最初に成功したものを返す
Promise.any(promises)
.then((first) => console.log("First success:", first))
.catch((error) => console.error("All failed:", error));
2. Async/Await
基本的な使用:
// async 関数は常に Promise を返す
async function fetchUser(id) {
const response = await fetch(`/api/users/${id}`);
const user = await response.json();
return user;
}
// try/catch でエラー処理
async function getUserData(id) {
try {
const user = await fetchUser(id);
const posts = await fetchUserPosts(user.id);
return { user, posts };
} catch (error) {
console.error("Error fetching data:", error);
throw error;
}
}
// 順序実行 vs 並列実行
async function sequential() {
const user1 = await fetchUser(1); // 待つ
const user2 = await fetchUser(2); // その後待つ
return [user1, user2];
}
async function parallel() {
const [user1, user2] = await Promise.all([fetchUser(1), fetchUser(2)]);
return [user1, user2];
}
高度なパターン:
// async IIFE
(async () => {
const result = await someAsyncOperation();
console.log(result);
})();
// async イテレーション
async function processUsers(userIds) {
for (const id of userIds) {
const user = await fetchUser(id);
await processUser(user);
}
}
// トップレベル await (ES2022)
const config = await fetch("/config.json").then((r) => r.json());
// リトライロジック
async function fetchWithRetry(url, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
return await fetch(url);
} catch (error) {
if (i === retries - 1) throw error;
await new Promise((resolve) => setTimeout(resolve, 1000 * (i + 1)));
}
}
}
// タイムアウトラッパー
async function withTimeout(promise, ms) {
const timeout = new Promise((_, reject) =>
setTimeout(() => reject(new Error("Timeout")), ms),
);
return Promise.race([promise, timeout]);
}
関数型プログラミングパターン
JavaScript における関数型プログラミングは、純粋関数、不変性、および構成可能な変換を中心にしています。
references/advanced-patterns.md で扱う主なトピック:
- 配列メソッド —
map,filter,reduce,find,findIndex,some,every,flatMap,Array.from - 高階関数 — カスタム
forEach/map/filter、カリー化、部分適用、メモ化 - 合成とパイピング —
compose/pipeユーティリティと実践的なデータ変換の例 - 純粋関数と不変性 — 不変配列/オブジェクト操作、
structuredCloneを使用したディープクローン
最新のクラス機能
ES2022 のクラスは、プライベートフィールド (#field)、静的フィールド、getter/setter、プライベートメソッドをサポートします。継承の完全な例については references/advanced-patterns.md を参照してください。
モジュール (ES6)
// 名前付きエクスポート
export const PI = 3.14159;
export function add(a, b) { return a + b; }
// デフォルトエクスポート
export default function multiply(a, b) { return a * b; }
// インポート
import multiply, { PI, add } from "./math.js";
// 動的インポート (コード分割)
const { add } = await import("./math.js");
再エクスポート、名前空間インポート、条件付き動的読み込みについては references/advanced-patterns.md を参照してください。
イテレータとジェネレータ
ジェネレータ (function*) と async ジェネレータ (async function*) は遅延シーケンスと async ページングを有効にします。カスタムイテレータ、range ジェネレータ、フィボナッチ、for await...of の例については references/advanced-patterns.md を参照してください。
最新の演算子
// オプショナルチェーン — 安全なプロパティアクセス
const city = user?.address?.city;
const result = obj.method?.();
// Nullish 合体 — null/undefined の場合のみデフォルト (0 や "" ではない)
const value = null ?? "default"; // 'default'
const zero = 0 ?? "default"; // 0
// 論理的割り当て
a ??= "default"; // null/undefined の場合に割り当て
obj.count ||= 1; // falsy の場合に割り当て
obj.count &&= 2; // truthy の場合に割り当て
パフォーマンス最適化
デバウンス、スロットル、ジェネレータを使用した遅延評価については references/advanced-patterns.md を参照してください。
ベストプラクティス
- デフォルトで const を使用: 再割り当てが必要な場合のみ let を使用する
- アロー関数を優先: 特にコールバックの場合
- テンプレートリテラルを使用: 文字列連結の代わりに
- オブジェクトと配列を分割代入: よりクリーンなコードのために
- async/await を使用: Promise チェーンの代わりに
- データを変更しない: スプレッド演算子と配列メソッドを使用する
- オプショナルチェーンを使用: "Cannot read property of undefined" を防ぐ
- Nullish 合体を使用: デフォルト値の場合
- 配列メソッドを優先: 従来のループの代わりに
- モジュールを使用: コード構成を改善するために
- 純粋関数を書く: テストと推論が容易
- 意味のある変数名を使用: 自己ドキュメンテーション的なコード
- 関数を小さく保つ: 単一責任原則
- エラーを適切に処理: async/await で try/catch を使用
- 厳密モードを使用:
'use strict'でエラー検出を改善
一般的な落とし穴 (this バインディング、Promise のアンチパターン、メモリリーク) については references/advanced-patterns.md を参照してください。
ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- wshobson
- リポジトリ
- wshobson/agents
- ライセンス
- MIT
- 最終更新
- 不明
Source: https://github.com/wshobson/agents / ライセンス: MIT
関連スキル
doubt-driven-development
重要な判断はすべて、本番環境への展開前に新しい視点から対抗的レビューを実施します。速度より正確性が重要な場合、不慣れなコードを扱う場合、本番環境・セキュリティに関わるロジック・取り消し不可の操作など影響度が高い場合、または後でバグを修正するよりも今検証する方が効率的な場合に活用してください。
apprun-skills
TypeScriptを使用したAppRunアプリケーションのMVU設計に関する総合的なガイダンスが得られます。コンポーネントパターン、イベントハンドリング、状態管理(非同期ジェネレータを含む)、パラメータと保護機能を備えたルーティング・ナビゲーション、vistestを使用したテストに対応しています。AppRunコンポーネントの設計・レビュー、ルートの配線、状態フローの管理、AppRunテストの作成時に活用してください。
desloppify
コードベースのヘルスチェックと技術負債の追跡ツールです。コード品質、技術負債、デッドコード、大規模ファイル、ゴッドクラス、重複関数、コードスメル、命名規則の問題、インポートサイクル、結合度の問題についてユーザーが質問した場合に使用してください。また、ヘルススコアの確認、次の改善項目の提案、クリーンアップ計画の作成をリクエストされた際にも対応します。29言語に対応しています。
debugging-and-error-recovery
テストが失敗したり、ビルドが壊れたり、動作が期待と異なったり、予期しないエラーが発生したりした場合に、体系的な根本原因デバッグをガイドします。推測ではなく、根本原因を見つけて修正するための体系的なアプローチが必要な場合に使用してください。
test-driven-development
テスト駆動開発により実装を進めます。ロジックの実装、バグの修正、動作の変更など、あらゆる場面で活用できます。コードが正常に動作することを証明する必要がある場合、バグ報告を受けた場合、既存機能を修正する予定がある場合に使用してください。
incremental-implementation
変更を段階的に実施します。複数のファイルに影響する機能や変更を実装する場合に使用してください。大量のコードを一度に書こうとしている場合や、タスクが一度では完結できないほど大きい場合に活用します。