JavaScript | ES6+ 文法:関数の進化 – 高階関数との相性

JavaScript
スポンサーリンク

高階関数とは何か

高階関数は「関数を“引数として受け取る”、または“関数を返す”関数」のことです。関数を値として扱えることで、ロジックの差し替え・再利用・合成が簡単になります。ここが重要です:ES6+ ではアロー関数、デフォルト引数、残余引数、レキシカル this がそろい、短く安全に高階関数を使えるようになりました。

// 関数を“受け取る”高階関数
function process(rows, mapFn) {
  return rows.map(mapFn); // mapFn は外から注入
}

// 関数を“返す”高階関数
const makeAdder = x => y => x + y;
const add10 = makeAdder(10);
console.log(add10(5)); // 15
JavaScript

ES6+で相性が良くなった理由

アロー関数でコールバックが短くなる

アロー関数は「(引数) => 式」の形で、暗黙の return を使えば1行で意図が伝わります。ここが重要です:高階関数に渡すコールバックが短く、ノイズ(function/return)が減ります。

const labels = rows => rows.map(r => `${r.id}:${r.name.trim()}`);
JavaScript

レキシカルthisで「文脈」が安定する

アロー関数は this を“定義場所”に固定します。高階関数へコールバックを渡しても this 迷子になりません。ここが重要です:クラスやオブジェクトの内部状態を扱うコールバックでも安全です。

class Counter {
  constructor() { this.value = 0; }
  start() { setInterval(() => { this.value++; }, 1000); } // this が固定
}
JavaScript

デフォルト引数・残余引数で柔軟なインターフェース

高階関数の“可変部分”に既定値や可変長を与えて、呼び出し側を短くできます。ここが重要です:よくある使い方を既定にし、必要なときだけ上書きさせる。

function transform(rows, mapFn = r => r, ...extras) {
  return rows.map(r => mapFn(r, ...extras));
}
JavaScript

配列メソッドは高階関数の宝庫

map・filter・reduce(鉄板の3兄弟)

配列メソッドは「関数を渡して処理を委ねる」典型です。ここが重要です:変換は map、絞り込みは filter、集計は reduce に役割分担すると読みやすくなります。

const rows = [
  { id: 1, active: true,  price: 100 },
  { id: 2, active: false, price: 200 },
  { id: 3, active: true,  price: 300 },
];

const view = rows
  .filter(r => r.active)
  .map(r => ({ id: r.id, tax: Math.round(r.price * 0.1) }));

const total = rows.reduce((sum, r) => sum + r.price, 0);
JavaScript

sort・flatMap(ひと味違う応用)

sort は比較関数を渡す高階関数、flatMap は map の結果を平坦化します。ここが重要です:比較・展開の戦略をコールバックで注入する。

// sort:比較関数を差し替え可能
const byPriceAsc = (a, b) => a.price - b.price;
const sorted = [...rows].sort(byPriceAsc);

// flatMap:変換しつつ配列を平坦化
const tokens = ["a b", "c", "d e"].flatMap(s => s.split(" ")); // ["a","b","c","d","e"]
JavaScript

高階関数を自分で設計する

関数を受け取るAPI(戦略の差し替え)

“可変部分”をコールバックにして差し替え可能にします。ここが重要です:テスト容易性と拡張性が上がる設計になります。

function processRows(rows, mapFn, filterFn = () => true) {
  return rows.filter(filterFn).map(mapFn);
}

// 用途に応じて戦略だけ差し替える
const cards = processRows(
  rows,
  r => `${r.id}:${r.price}`,
  r => r.active
);
JavaScript

関数を返すAPI(カリー化・部分適用)

設定を先に固定して、あとでデータだけ渡せる関数を返します。ここが重要です:複数箇所で同じ設定を使うときに便利です。

const makeTaxMapper = (rate = 0.1, round = Math.round) =>
  row => ({ id: row.id, priceWithTax: round(row.price * (1 + rate)) });

const map10 = makeTaxMapper(0.1);
const view2 = rows.map(map10);
JavaScript

よくある落とし穴と対策

this を失う呼び出し

メソッドを直接コールバックに渡すと this が消えます。ここが重要です:アローで包むか bind で固定する。

const view = { id: 42, render() { console.log(this.id); } };
setTimeout(() => view.render(), 0);           // OK
// setTimeout(view.render, 0);                // NG
JavaScript

無理な1行化で可読性低下

暗黙の return に詰め込みすぎると意図が伝わりません。複雑なら波括弧+return に切り替える。

const calc = x => {
  const base = x > 0 ? x * 2 : x / 2;
  const bonus = Math.max(0, x - 10);
  return (base + bonus) | 0;
};
JavaScript

副作用の扱い

高階関数は“純粋な変換”に向いています。副作用(ログ・DOM操作)は分離し、変換・集計のコールバックは可能な限り純粋に。

// 変換は純粋に、描画は別フェーズで
const view3 = rows.map(r => ({ id: r.id, price: r.price * 2 }));
view3.forEach(v => console.log(v));
JavaScript

例題で理解を固める

// 1) フィルタ+マップ:外部設定を関数として注入
const applyTax = (rate = 0.1, round = Math.round) =>
  r => ({ id: r.id, priceWithTax: round(r.price * (1 + rate)) });

const onlyActive = r => r.active;
const result = rows.filter(onlyActive).map(applyTax(0.08, Math.ceil));

// 2) 検索クエリビルダー:関数を返して部分適用
const makeQuery = base => params =>
  `${base}?${new URLSearchParams(params)}`;

const search = makeQuery("/search");
console.log(search({ q: "js", page: 2 }));

// 3) 安全なパイプライン:関数を合成して段階処理
const pipe = (...fns) => x => fns.reduce((v, f) => f(v), x);
const trim = s => s.trim();
const lower = s => s.toLowerCase();
const toSlug = s => s.replace(/\s+/g, "-");

const slugify = pipe(trim, lower, toSlug);
console.log(slugify("  Hello World  ")); // "hello-world"
JavaScript

まとめ

高階関数の核心は「ロジックを“関数の受け渡し”で差し替え・合成できる」ことです。ES6+ はアロー関数で短く、レキシカル this で安全に、デフォルト引数・残余引数で柔軟に設計できます。配列メソッドを軸に、関数を受け取る・返すAPIを組み、this 失い・副作用・過剰な1行化を避ける。この指針を徹底すれば、初心者でも直感的で保守しやすい関数設計を実現できます。

タイトルとURLをコピーしました