実務向け forEach テンプレート
// 1) 基本(値・インデックス・配列の3引数)
arr.forEach((v, i, a) => {
// v: 要素, i: インデックス, a: 配列(元の参照)
});
// 2) this を使いたい(第2引数 thisArg)
const ctx = { rate: 1.1 };
prices.forEach(function (p) {
this.rate; // 1.1
}, ctx);
// 3) 早期終了が不要な一括処理(ログ、累積、副作用)
logs.forEach(e => logger.info(e));
// 4) 入力検証(null/undefined 防御)
(arr ?? []).forEach(v => process(v));
// 5) 例外集約(ループは継続、失敗だけ蓄積)
const errors = [];
items.forEach(v => {
try {
handle(v);
} catch (e) {
errors.push({ v, e });
}
});
// エラーまとめて扱う
if (errors.length) report(errors);
JavaScript
性能とセキュリティのベストプラクティス
- 用途の適材適所:
- 副作用目的(ログ、DOM 操作、外部 I/O)なら forEach は読みやすい。
- 変換やフィルタは
map/filter/reduce を使うと意図が明確で中間配列の管理も不要。
- 早期終了不可:
- forEach は
break/continue/return で反復を止められない。探索や条件成立後に終了したいなら for/for...of/some/every/find を選ぶ。
- 非同期の落とし穴:
await arr.forEach(...) は待機しない。逐次処理は for...of、並列は Promise.all+分割や並列数制御を使う。
- 破壊的操作を避ける:
- 走査中に
splice/push で同じ配列を変更すると意図しない挙動や性能劣化。必要なら別の出力配列へ書き出す。
- 疎配列と境界:
new Array(10) のような疎配列は空要素をスキップする。期待通りに回したいなら初期化してから使う。
- 入力検証とエスケープ:
- 外部入力由来の配列は型チェック、文字列を UI/DB/HTML に渡すなら必ずエスケープやパラメータ化でインジェクション対策。
- 大規模入力対策:
- 巨大配列はチャンク処理・ストリーミング・仮想リストでメモリと描画の負荷を分散。
- メモリ割り当て削減:
- ホットパスではクロージャの過剰生成を避け、必要なら外で関数を定義して再利用。
React の実務テンプレート
// 1) リスト表示は map(宣言的)
function UserList({ users }) {
return (
<ul>
{users.map(u => (
<li key={u.id}>{u.name}</li>
))}
</ul>
);
}
// 2) 副作用前処理は forEach、描画は map
function Orders({ orders }) {
const rows = [];
orders.forEach(o => {
const total = Math.max(0, o.qty) * Math.max(0, o.price);
rows.push({
id: o.id,
label: `${o.item} x${o.qty}`,
total,
expensive: total >= 20000,
});
});
return (
<section>
{rows.map(r => (
<article key={r.id} className={r.expensive ? 'highlight' : ''}>
<header>{r.label}</header>
<div>¥{r.total.toLocaleString()}</div>
</article>
))}
</section>
);
}
// 3) 非同期送信(並列数制御は後述)
async function submitBulk(items) {
// 逐次処理は for...of を推奨(forEach は await できない)
for (const item of items) {
await fetch('/api/item', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(item),
});
}
}
JSX
- ラベル: 安定した key(ID)を使う。
- 分離: 重い前処理はロジック側、描画は宣言的に。
Vue の実務テンプレート
<template>
<ul>
<li v-for="u in viewUsers" :key="u.id">
{{ u.name }} — {{ u.role ?? 'N/A' }}
</li>
</ul>
</template>
<script setup>
import { computed } from 'vue';
const props = defineProps({ users: { type: Array, required: true } });
const viewUsers = computed(() => {
const out = [];
(props.users ?? []).forEach(u => {
out.push({ id: u.id, name: u.name.trim(), role: u.role });
});
return out;
});
</script>
HTML
- computed 前処理: 整形は forEach でも可、UI は
v-for。
- 大量表示: 仮想スクロール(virtual list)導入を検討。
Node.js の実務テンプレート
// 1) 逐次 I/O は for...of(forEach + await は不可)
export async function processSequential(items, handle) {
for (const v of items) {
await handle(v);
}
}
// 2) チャンク処理(メモリ・レート制限に配慮)
export async function processInChunks(items, handle, size = 1000) {
for (let i = 0; i < items.length; i += size) {
const chunk = items.slice(i, i + size);
for (const v of chunk) {
await handle(v);
}
}
}
// 3) 例外を集約しつつ継続
export async function safeProcess(items, handle) {
const errors = [];
items.forEach(v => {
try {
handle(v);
} catch (e) {
errors.push({ v, e });
}
});
return errors;
}
JavaScript
- 並列制御: レート制限や接続上限を守るため、後述のミニライブラリで制御。
- 入力検証:
Array.isArray とスキーマチェックで防御。
ステップバイステップ具体実装(注文配列の整形/表示/保存)
1. 型と検証を定義
type Order = { id: string; item: string; qty: number; price: number; tags?: string[] };
export function isOrder(x: any): x is Order {
return x
&& typeof x.id === 'string'
&& typeof x.item === 'string'
&& Number.isFinite(x.qty)
&& Number.isFinite(x.price);
}
TypeScript
2. forEach で前処理(負値防御・整形)
export function preprocessOrders(orders) {
const out = [];
(Array.isArray(orders) ? orders : []).forEach(o => {
if (!isOrder(o)) return;
const qty = Math.max(0, o.qty);
const price = Math.max(0, o.price);
const total = qty * price;
out.push({
id: o.id,
label: `${o.item} x${qty}`,
total,
expensive: total >= 20000,
tags: Array.isArray(o.tags) ? o.tags.filter(Boolean) : [],
});
});
return out;
}
JavaScript
3. React で表示(宣言的)
function OrderList({ orders }) {
const rows = preprocessOrders(orders);
return (
<section>
{rows.map(r => (
<article key={r.id} className={r.expensive ? 'highlight' : ''}>
<header>{r.label}</header>
<div>¥{r.total.toLocaleString()}</div>
{!!r.tags.length && <small>Tags: {r.tags.join(', ')}</small>}
</article>
))}
</section>
);
}
JSX
4. Vue で表示(computed + v-for)
<template>
<section>
<article v-for="r in rows" :key="r.id" :class="{ highlight: r.expensive }">
<header>{{ r.label }}</header>
<div>¥{{ r.total.toLocaleString() }}</div>
<small v-if="r.tags?.length">Tags: {{ r.tags.join(', ') }}</small>
</article>
</section>
</template>
<script setup>
import { computed } from 'vue';
import { preprocessOrders } from './orders.js';
const props = defineProps({ orders: { type: Array, required: true } });
const rows = computed(() => preprocessOrders(props.orders));
</script>
HTML
5. Node.js で保存(チャンク+トランザクション)
import { preprocessOrders } from './orders.js';
export async function saveOrders(db, orders) {
const rows = preprocessOrders(orders);
const CHUNK = 1000;
for (let i = 0; i < rows.length; i += CHUNK) {
const chunk = rows.slice(i, i + CHUNK);
await db.transaction(async tx => {
for (const r of chunk) {
await tx.insert({
id: r.id,
label: r.label,
total: r.total,
tags: r.tags,
});
}
});
}
}
JavaScript
小さなライブラリ推奨とミニユーティリティ
- 並列数制御の導入:
- p-limit 相当の軽量実装で I/O の同時実行を制御(例:5〜10並列)。
- 部分導入の方針:
- lodash-es を個別 import(例:
import chunk from 'lodash-es/chunk')でバンドルを軽量化。
- 再利用ヘルパの抽出:
- よく使うパターンを小さな関数に切り出し、重複を排除。
// 1) 簡易 p-limit(軽量並列制御)
export function createLimiter(limit = 5) {
const queue = [];
let active = 0;
const run = async fn => {
active++;
try { return await fn(); }
finally {
active--;
if (queue.length) queue.shift()();
}
};
return fn =>
active < limit
? run(fn)
: new Promise(res => queue.push(() => res(run(fn))));
}
// 使い方
const limit = createLimiter(5);
await Promise.all(items.map(item => limit(() => doAsync(item))));
// 2) safeForEach(例外を集約)
export function safeForEach(arr, fn) {
const errors = [];
(arr ?? []).forEach((v, i) => {
try { fn(v, i); } catch (e) { errors.push({ i, v, e }); }
});
return errors;
}
// 3) toRows(整形テンプレ)
export function toRows(orders) {
const out = [];
(orders ?? []).forEach(o => {
if (!isOrder(o)) return;
const t = Math.max(0, o.qty) * Math.max(0, o.price);
out.push({ id: o.id, label: `${o.item} x${o.qty}`, total: t });
});
return out;
}
JavaScript
直感的な選び方の指針
- 副作用中心なら forEach。
- 変換・抽出なら map/filter/reduce。
- 早期終了・逐次 async が必要なら for/for…of。