実務向け for ループテンプレート
// 1) 基本(length キャッシュで安定)
for ( let i = 0 , len = arr.length; i < len; i ++ ) {
const item = arr[i];
// ...処理
}
// 2) 早期終了(探索など)
for ( let i = 0 , len = arr.length; i < len; i ++ ) {
const v = arr[i];
if ( matches (v)) {
handle (v);
break ;
}
}
// 3) 後ろから走査(削除・差し替えに強い)
for ( let i = arr.length - 1 ; i >= 0 ; i -- ) {
const v = arr[i];
if ( shouldRemove (v)) arr. splice (i, 1 );
}
// 4) continue でネスト削減
for ( let i = 0 , len = arr.length; i < len; i ++ ) {
const v = arr[i];
if ( ! isValid (v)) continue ;
process (v);
}
// 5) 例外をまとめて処理(ループ内 throw を外で捕捉)
try {
for ( let i = 0 , len = arr.length; i < len; i ++ ) {
mayThrow (arr[i]);
}
} catch (err) {
report (err);
}
// 6) for-of(可読性優先の走査)
for ( const v of arr) {
process (v);
}
// 7) インデックスと値が欲しい場合(for-of + entries)
for ( const [i, v] of arr. entries ()) {
process (i, v);
}
JavaScript
性能とセキュリティのベストプラクティス
長さのキャッシュ: for (let i = 0, len = arr.length; i < len; i++) のように長さを初期化時にキャッシュする。
破壊的操作の管理: 走査中の splice/push はインデックスを壊しやすい。後ろから走査 か新配列に書き出し で安全に処理する。
早期終了とガード: 不要な反復を避けるために break/continue を積極活用し、上位で入力検証してからループに入る。
メモリアロケーション削減: ホットループで新しい配列やオブジェクトを毎回作らない。必要なら事前に new Array(len) で容量を確保して書き込む。
I/O と CPU の分離: ループ内の非同期 I/O はまとめてバッチ化(例: 100〜1000単位)し、CPU処理は同期で一気に回す。
境界・型チェック: 外部入力に由来する配列は Array.isArray と各要素の型を検証。null/undefined を許容するか方針を決める。
XSS/インジェクション対策: ループで文字列を生成して UI/DB に渡す場合は必ずエスケープ・パラメータ化を行う。テンプレート文字列をそのまま SQL/HTML に入れない。
巨大入力の防御: 上限件数・総サイズを設定。サーバ側はストリーミング/ページング、クライアント側は仮想リストや段階読み込みを使う。
安定キーの使用(UI): React/Vue のリスト表示で index を key にしない。並べ替え・挿入で差分が壊れるため、安定 ID を使う。
React の実装テンプレート
// 1) 宣言的レンダリング(最小・高速)
function UserList ({ users }) {
return (
< ul >
{ users. map ( u => (
< li key ={ u.id } > { u.name } </ li >
)) }
</ ul >
);
}
// 2) 重い前処理は for、描画は map
function Orders ({ orders }) {
const rows = new Array (orders.length);
for ( let i = 0 , len = orders.length; i < len; i ++ ) {
const o = orders[i];
rows[i] = {
id: o.id,
label: ` ${ o.item } x ${ o.qty } ` ,
total: o.price * o.qty,
expensive: o.price * o.qty >= 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) 非同期バッチ処理(UI イベントで)
async function handleSubmit ( items ) {
const CHUNK = 500 ;
for ( let i = 0 , len = items.length; i < len; i += CHUNK) {
const chunk = items. slice (i, i + CHUNK);
await fetch ( '/api/bulk' , {
method: 'POST' ,
headers: { 'Content-Type' : 'application/json' },
body: JSON. stringify (chunk),
});
}
}
JSX
安定 key: 要素固有の ID を使う。
副作用とレンダリングの分離: 重い処理は useMemo でメモ化、もしくはレンダリング前に for で前処理。
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 src = Array. isArray (props.users) ? props.users : [];
const out = new Array (src.length);
for ( let i = 0 , len = src.length; i < len; i ++ ) {
const u = src[i];
out[i] = { 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: 描画は v-for、整形は for+computed に分ける。
パフォーマンス: 大規模リストは仮想化を検討。
Node.js の実装テンプレート
// 1) 大量配列をチャンクで処理(I/O バックプレッシャー対応)
export async function processInChunks ( items , handle , size = 1000 ) {
for ( let i = 0 , len = items.length; i < len; i += size) {
const chunk = items. slice (i, i + size);
for ( let j = 0 , jlen = chunk.length; j < jlen; j ++ ) {
await handle (chunk[j]); // I/O は await、CPU は同期でまとめる
}
}
}
// 2) ストリーミング(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);
}
}
// 3) トランザクションでバルク書き込み
export async function saveBulk ( db , rows , chunk = 1000 ) {
for ( let i = 0 , len = rows.length; i < len; i += chunk) {
const part = rows. slice (i, i + chunk);
await db. transaction ( async tx => {
for ( let j = 0 , jlen = part.length; j < jlen; j ++ ) {
await tx. insert (part[j]);
}
});
}
}
JavaScript
入力検証: Array.isArray と要素のスキーマチェックで防御。
DoS 防御: 件数上限・タイムアウト・並列数制限(例: p-limit)を設定。
ステップバイステップの具体実装(注文配列を加工して表示と保存)
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. for で前処理(負値防御・集計)
export function preprocessOrders ( orders ) {
const src = Array. isArray (orders) ? orders : [];
const out = new Array (src.length);
for ( let i = 0 , len = src.length; i < len; i ++ ) {
const o = src[i];
if ( ! isOrder (o)) {
out[i] = null ;
continue ;
}
const qty = Math. max ( 0 , o.qty);
const price = Math. max ( 0 , o.price);
const total = qty * price;
out[i] = {
id: o.id,
label: ` ${ o.item } x ${ qty } ` ,
total,
expensive: total >= 20000 ,
tags: Array. isArray (o.tags) ? o.tags. filter (Boolean) : [],
};
}
return out. filter (Boolean);
}
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 で表示
< 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 , len = rows.length; i < len; i += CHUNK) {
const chunk = rows. slice (i, i + CHUNK);
await db. transaction ( async tx => {
for ( let j = 0 , jlen = chunk.length; j < jlen; j ++ ) {
await tx. insert ({
id: chunk[j].id,
label: chunk[j].label,
total: chunk[j].total,
tags: chunk[j].tags,
});
}
});
}
}
JavaScript
役立つミニユーティリティ
export function * enumerate ( arr ) {
for ( let i = 0 , len = arr.length; i < len; i ++ ) {
yield [i, arr[i]];
}
}
JavaScript
export function findMap ( arr , pred , map ) {
for ( let i = 0 , len = arr.length; i < len; i ++ ) {
const v = arr[i];
if ( pred (v, i)) return map (v, i);
}
return null ;
}
JavaScript
compactMap(null/undefined を除外)
export function compactMap ( arr , fn ) {
const out = [];
for ( let i = 0 , len = arr.length; i < len; i ++ ) {
const v = fn (arr[i], i);
if (v != null ) out. push (v);
}
return out;
}
JavaScript
軽量ライブラリの扱い
部分導入: Lodash は必要関数だけ個別インポート(例: import map from 'lodash-es/map')。
代替の小粒: p-limit(並列制御)、tiny-invariant(入力検証)などを用途別に採用。