Why not login to Qiita and try out its useful features?

We'll deliver articles that match you.

You can read useful information later.

2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

JavaScriptにおけるパフォーマンス設計:計算最適化・メモ化・イベント制御・描画抑制の戦略的アプローチ

Posted at

概要

速さとは“副産物”ではなく“設計”である。
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描画プロパティとレイアウトスラッシング対策を実施


結語

パフォーマンスとは“速度”ではなく“設計のバランス”である。

  • 処理を遅らせる勇気
  • 頻度を制御する冷静さ
  • 描画をまとめる設計力

速いコードではなく、
「速さが継続する構造」こそが、真のパフォーマンス設計である。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up

Qiita Conference 2025 will be held!: 4/23(wed) - 4/25(Fri)

Qiita Conference is the largest tech conference in Qiita!

Keynote Speaker

ymrl、Masanobu Naruse, Takeshi Kano, Junichi Ito, uhyo, Hiroshi Tokumaru, MinoDriven, Minorun, Hiroyuki Sakuraba, tenntenn, drken, konifar

View event details
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?