概要
速さとは“副産物”ではなく“設計”である。
JavaScriptのパフォーマンスは、コードの書き方よりも、処理の設計・頻度の制御・無駄な描画の削減により決定される。
本稿では、UIレスポンスを保ったまま処理を最適化するために、以下の観点から戦略を体系化する:
- 演算コストの見積もりと分割
- メモ化(Memoization)の正しい適用
- スクロール/リサイズ等の高頻度イベントの制御
- 再描画の抑制と構造最適化
- 非同期処理と処理バッチ化
1. 計算コストの局所化と分割戦略
// ❌ 毎回重い計算を通る構造
function expensiveFilter(arr) {
return arr.filter(item => heavyComputation(item));
}
✅ 解決策:
- 計算を遅延化 or キャッシュ化
- 結果を次回に再利用(メモ化)
const memo = new Map();
function memoizedFilter(arr) {
const key = JSON.stringify(arr);
if (memo.has(key)) return memo.get(key);
const result = arr.filter(item => heavyComputation(item));
memo.set(key, result);
return result;
}
2. イベントバースト制御(デバウンス / スロットル)
✅ デバウンス:最後の操作だけを処理
function debounce(fn, delay) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), delay);
};
}
✅ スロットル:一定間隔で処理
function throttle(fn, interval) {
let last = 0;
return (...args) => {
const now = Date.now();
if (now - last >= interval) {
last = now;
fn(...args);
}
};
}
→ ✅ スクロール・リサイズ・入力監視などのリアルタイム処理に不可欠
3. DOM操作のバッチ化と再描画制御
// ❌ 連続DOM操作
for (let i = 0; i < 1000; i++) {
const el = document.createElement('li');
el.textContent = `Item ${i}`;
list.appendChild(el);
}
✅ DocumentFragment + requestAnimationFrame
const fragment = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
const el = document.createElement('li');
el.textContent = `Item ${i}`;
fragment.appendChild(el);
}
requestAnimationFrame(() => list.appendChild(fragment));
- ✅ DOM変更は一括で行う
- ✅ 描画タイミングに合わせて最適化
4. レンダリング最適化:スタイル・レイアウト・ペイント分離
- ✅ スタイル変更は一括して書き換える
- ✅
style.transform
,opacity
などGPUレイヤに委ねられるプロパティを使用 - ❌ 連続で
offsetTop
,getBoundingClientRect()
を参照・更新 → レイアウトスラッシングに注意
5. メモリリークと監視対象の明示的解除
// ❌ イベントリスナーを永続化
window.addEventListener('resize', handler);
// ✅ removeEventListenerで明示的解除
window.removeEventListener('resize', handler);
- ✅ モーダル・非表示エリアなどで不要になった監視は破棄
- ✅ IntersectionObserverやMutationObserverもdisconnect()を設計に組み込む
設計判断フロー
① 計算が重い? → メモ化 or 遅延処理を検討
② 高頻度イベントがある? → debounce/throttle で頻度制限
③ DOM操作が大量? → DocumentFragment や requestAnimationFrame で一括描画
④ レンダリングが遅い? → GPU最適化プロパティ、再レイアウト回避
⑤ 見えないイベントが残っている? → removeEventListener / disconnect() を設計に含める
よくあるミスと対策
❌ ユーザー操作ごとに即座にAPI呼び出し
→ ✅ debounceで操作の意図が明確になってから処理
❌ ページ遷移後もイベントが残り続ける
→ ✅ ライフサイクルでイベント解除の設計を含める
❌ アニメーションがカクつく
→ ✅ GPU描画プロパティとレイアウトスラッシング対策を実施
結語
パフォーマンスとは“速度”ではなく“設計のバランス”である。
- 処理を遅らせる勇気
- 頻度を制御する冷静さ
- 描画をまとめる設計力
速いコードではなく、
「速さが継続する構造」こそが、真のパフォーマンス設計である。