実務向け for…of テンプレート
// 1) 基本(値を直接扱う)
for (const v of arr) {
// v を処理
}
// 2) インデックスも必要なら entries()
for (const [i, v] of arr.entries()) {
// i と v を処理
}
// 3) 早期終了(条件成立で break)
for (const v of arr) {
if (shouldStop(v)) break;
handle(v);
}
// 4) ネスト削減(continue)
for (const v of arr) {
if (!isValid(v)) continue;
process(v);
}
// 5) 非同期処理(逐次 await)
for (const v of arr) {
await doAsync(v); // バッチ/並列は後述
}
// 6) 反復可能オブジェクト(Map/Set など)
const map = new Map([['a', 1], ['b', 2]]);
for (const [key, val] of map) {
// key と val を処理
}
const set = new Set([1, 2, 3]);
for (const v of set) {
// v を処理
}
JavaScriptfor…of は配列、文字列、TypedArray、Map/Set、NodeList、ジェネレータなど「反復可能オブジェクト」を自然に走査できる構文です。主要ブラウザで 2015 年以降広く提供されています。
性能とセキュリティのベストプラクティス
- 値ベースの走査: for…of は値を直接受け取るため、
arr[i]のインデックスアクセスより読みやすく、entries()で必要に応じてインデックスも取得します。 - 破壊的操作を避ける: ループ中に同じ配列へ
splice/pushを行うと意図しないスキップや重複が起こります。削除は「後ろからの for」か、別配列へ書き出す戦略に切り替えます。 - 早期終了/ガード:
breakとcontinueで無駄な反復を抑え、前段で入力検証を済ませてからループに入ります。 - I/O と並列制御: 逐次
awaitは簡単ですが遅くなりがち。サイズや API 上限に合わせてバッチ化や並列数制限(例:p-limit)を使い、スループットと安定性を両立します。 - 巨大入力の防御: 上限件数を設け、サーバーではストリーミング/ページング、クライアントでは仮想リストを検討します。
- XSS/インジェクション対策: ループで生成した文字列を HTML/SQL に流す場合は必ずエスケープやパラメータ化を行い、テンプレート文字列を生で埋め込まない。
- UI のキー安定性: React/Vue のリスト表示は宣言的に
map/v-forを使い、keyは安定した ID を付与します。JSX に直接 for を書くより、前処理と描画を分離するのが一般的です。
React の実務テンプレート
// 1) 宣言的レンダリング(推奨)
function UserList({ users }) {
return (
<ul>
{users.map(u => (
<li key={u.id}>{u.name}</li>
))}
</ul>
);
}
// 2) 重い前処理は for...of、描画は map
function Orders({ orders }) {
const rows = [];
for (const o of orders) {
const total = o.price * o.qty;
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) {
const CHUNK = 500;
for (let i = 0; i < items.length; i += CHUNK) {
const chunk = items.slice(i, i + CHUNK);
await fetch('/api/bulk', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(chunk),
});
}
}
JSXReact のリスト描画は
mapが定番。for は 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 = [];
for (const u of props.users ?? []) {
out.push({ id: u.id, name: u.name.trim(), role: u.role });
}
return out;
});
</script>
HTML<!-- 大規模リストは仮想スクロール(例: vue-virtual-scroller など) -->
<virtual-list :items="rows" :item-size="48">
<template #default="{ item }">
<div :key="item.id">{{ item.label }}</div>
</template>
</virtual-list>
HTML- 前処理は computed: 整形は for…of+
computed、UI はv-forで宣言的に。 - 大量表示: 仮想リスト導入で DOM 負荷を抑えます。
Node.js の実務テンプレート
// 1) for...of + 逐次 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) ストリーミング(CSV 行単位)
import fs from 'node:fs';
import readline from 'node:readline';
export async function processCsv(path, onRow) {
const stream = fs.createReadStream(path, { encoding: 'utf8' });
const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
for await (const line of rl) {
const cols = line.split(',');
await onRow(cols);
}
}
JavaScript- 並列制御: 大量 I/O は適切な並列数制限を導入し、API のレート制限や接続上限を守ります。
- 入力検証:
Array.isArrayとスキーマチェックで防御。DoS 対策に件数上限・タイムアウトも設定します。
ステップバイステップ具体実装(注文配列の加工・表示・保存)
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);
}
TypeScript2. for…of で前処理(負値防御・整形)
export function preprocessOrders(orders) {
const out = [];
for (const o of Array.isArray(orders) ? orders : []) {
if (!isOrder(o)) continue;
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;
}
JavaScript3. 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>
);
}
JSX4. 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>
HTML5. 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小さなライブラリ推奨とミニユーティリティ
- 部分導入: 必要最低限のユーティリティだけ個別インポート(lodash-es のピンポイント import など)でバンドルを軽く保つ。
- 並列数制限: I/O の並列は p-limit 相当の軽量ツールを採用し、スループットと安定性を両立。
- 再利用ヘルパ: よく使うパターンを小さな関数に抽出して重複を削減。
// 安全な enumerate(インデックスと値)
export function* enumerate(arr) {
let i = 0;
for (const v of arr) yield [i++, v];
}
// findMap(条件成立時に変換して即返す)
export function findMap(arr, pred, map) {
for (const v of arr) {
if (pred(v)) return map(v);
}
return null;
}
// compactMap(null/undefined を除外して push)
export function compactMap(arr, fn) {
const out = [];
for (const v of arr) {
const r = fn(v);
if (r != null) out.push(r);
}
return out;
}
JavaScript