JavaScript:関数とアロー関数
1学習の目的
- 関数を定義して引数を渡し、return で結果を受け取れるようになる。同じ処理をコピペする必要がなくなる。
- アロー関数の書き方を身につけ、既定値引数やスコープを理解する。次章の map / filter はアロー関数なしには書けないため、ここが土台になる。
2基礎解説
| やりたいこと | 書き方 | 備考 |
|---|---|---|
| 関数を作る | function add(a, b) { } | 従来の書き方 |
| 短く書く | const add = (a, b) => a + b; | アロー関数。return 省略可 |
| 省略できる引数 | (price, rate = 0.1) => | 渡さなければ既定値 |
| 複数の値を返す | return [total, best]; | const [t, b] = f(); |
- console.log と return は別物。log は画面に出すだけ、return は値を呼び出し元に渡す。計算結果を後で使うなら return。
- return を書かない関数の戻り値は undefined。const r = f(5); が undefined になる事故はほぼこれが原因。
- アロー関数は本体が式1つなら { } と return を省略できる。n => n * 2 のように書ける。引数が1つならカッコも省略可。
- アロー関数は定義より前で呼べない。function 宣言は前でも呼べる(巻き上げ)が、この違いに頼らず使う前に定義するのが安全。
- 関数の中で作った変数は外から見えない(スコープ)。外へ渡したいときは必ず return する。
現場使用例:税込計算 withTax(price)、入力チェック validate(name)、データ整形 formatRow(row)。同じ処理を複数の画面で使い回すときは必ず関数にする。
// 従来の書き方
function greet(name) {
return `こんにちは、${name}さん`;
}
// アロー関数(式1つなら return を省略できる)
const add = (a, b) => a + b;
const double = n => n * 2;
console.log(greet("田中")); // こんにちは、田中さん
console.log(add(3, 7)); // 10
console.log(double(5)); // 10
// 既定値つき引数
const withTax = (price, rate = 0.1) => Math.round(price * (1 + rate));
console.log(withTax(1000)); // 1100
console.log(withTax(1000, 0.08)); // 1080
// 複数の値を返す
const stats = nums => [Math.max(...nums), nums.length];
const [max, count] = stats([3, 9, 5]);
console.log(max, count); // 9 3
3基本ドリル(10問)
出力 == こんにちは、田中さん
function greet(name) { return ... } の中で return する。呼び出しは console.log(greet("田中"))。
function greet(name) {
return `こんにちは、${name}さん`;
}
console.log(greet("田中"));
関数の最小形。定義しただけでは何も起きず、呼び出して初めて動く。
A. 0 B. 空文字 C. undefined D. エラー 選択★☆☆無料
解答 == C
「何も返さない」をJavaScriptはどう表すか。
C
戻り値なし=undefined。この undefined が後の計算に混ざって NaN を生むのが典型的なバグの連鎖。
出力 == 10
const add = (a, b) => a + b; と書く。式1つなので { } と return は不要。
const add = (a, b) => a + b; console.log(add(3, 7));
アロー関数は矢印 => の右に式を書くだけで、その値が自動的に返る。1行で書ける処理はこの形が読みやすい。
function area(w, h) {
____ w * h;
}
console.log(area(3, 4));
出力 == 12
計算結果を呼び出し元へ渡すキーワード。
return
ここを console.log にすると画面には12が出るが、関数の戻り値は undefined になる。使い回すなら return。
A. 10 B. undefined C. 5 D. エラー 選択★☆☆無料
解答 == B
画面に出ることと、値が返ることは同じか?
B
画面には10が出るが return が無いので r は undefined。{ } を書いた場合は return を省略できない点にも注意。
出力 == 1100 / 1080(2行)
const withTax = (price, rate = 0.1) => Math.round(price * (1 + rate)); と書く。
const withTax = (price, rate = 0.1) => Math.round(price * (1 + rate)); console.log(withTax(1000)); console.log(withTax(1000, 0.08));
既定値つき引数は「ほとんどの場合は同じ値、たまに変えたい」場面に最適。既定値のある引数は後ろに置くのが原則。
const double = n ____ n * 2; console.log(double(5));
出力 == 10
アロー関数の矢印。イコールと大なり記号の組み合わせ。
=>
引数が1つならカッコも省略できる。(n) => n * 2 と書いても同じ。
A. 100が出る B. ReferenceErrorになる C. undefinedが出る D. 0が出る 選択★★☆無料
解答 == B
関数の中で作った変数はどこまで生きているか。
B
関数内の変数は関数を抜けると消える(スコープ)。外へ持ち出したければ return するのが唯一の正攻法。
出力 == 合計17 最大9
配列で返す。受け取りは const [total, max] = stats(...) と分割代入する。
const stats = nums => {
let total = 0;
for (const n of nums) {
total += n;
}
return [total, Math.max(...nums)];
};
const [total, max] = stats([3, 9, 5]);
console.log(`合計${total} 最大${max}`);
配列で返して分割代入で受け取るのが2値を返す定番。項目が増えるなら { total, max } とオブジェクトで返すほうが順序を気にせず済む。
出力 == 合格 / 不合格(2行)
三項演算子を使えば1行で書ける。
const judge = score => (score >= 60 ? "合格" : "不合格"); console.log(judge(72)); console.log(judge(45));
JS04の条件分岐を関数に閉じ込めた形。判定ロジックを1箇所にまとめておけば、基準が変わってもここだけ直せばよい。
4実践シナリオ(5問)
3行。4,800円 → 送料600円 ほか
関数を定義してから for...of で回す。テンプレートリテラルの中で shippingFee(p) を直接呼べる。
const shippingFee = price => (price >= 5000 ? 0 : 600);
for (const p of [4800, 5000, 12000]) {
console.log(`${p.toLocaleString()}円 → 送料${shippingFee(p)}円`);
}
JS04で書いた送料判定を関数にした形。ループの中で関数を呼ぶと、判定ロジックとループが分離されて読みやすくなる。
出力 == BMI:26.4 → 肥満
return { bmi, judge } のように返し、const { bmi, judge } = bmiCheck(...) で受け取る。小数1桁は toFixed(1)。
const bmiCheck = (height, weight) => {
const value = weight / height ** 2;
let judge;
if (value >= 25) {
judge = "肥満";
} else if (value < 18.5) {
judge = "痩せ";
} else {
judge = "標準";
}
return { bmi: value.toFixed(1), judge };
};
const { bmi, judge } = bmiCheck(1.72, 78);
console.log(`BMI:${bmi} → ${judge}`);
オブジェクトで返すと受け取り側が順序を気にしなくてよい。JS07の分割代入がここで活きる。項目が3つ以上になったら配列よりオブジェクトが安全。
出力3行が完全一致
エラー条件を先に書いて即 return すると、else を書かずに済む。
const validate = (name, password) => {
if (!name) {
return "名前を入力してください";
}
if (password.length < 8) {
return "パスワードは8文字以上にしてください";
}
return "OK";
};
console.log(validate("", "abc123"));
console.log(validate("田中", "abc123"));
console.log(validate("田中", "abcd1234"));
早期リターンという書き方。return はその場で関数を抜けるので、else や入れ子の if が消えて一気に読みやすくなる。実務のバリデーションはほぼこの形。
finalPrice(12000, true, false) / finalPrice(12000) / finalPrice(400, false, true) の3通りを「12,000円 → 10,800円」の形式で出力せよ。 コーディング★★★無料
出力3行が完全一致
const finalPrice = (price, isMember = false, hasCoupon = false) => { ... } と定義する。中身はJS04の割引計算と同じ。
const finalPrice = (price, isMember = false, hasCoupon = false) => {
let total = price;
if (isMember) {
total = Math.round(total * 0.9);
}
if (hasCoupon) {
total -= 500;
}
if (total < 0) {
total = 0;
}
return total;
};
console.log(`12,000円 → ${finalPrice(12000, true, false).toLocaleString()}円`);
console.log(`12,000円 → ${finalPrice(12000).toLocaleString()}円`);
console.log(`400円 → ${finalPrice(400, false, true).toLocaleString()}円`);
既定値を false にしておくと「普通の客なら引数1つで呼べる」関数になる。よくある呼び方を短く書けるようにするのが良い関数設計。
出力2行が完全一致
return { average, max, passed } で3つ返す。2クラスは [["A組", classA], ["B組", classB]] の配列で回すとループ1本で書ける。平均は toFixed(1)。
const summarize = scores => {
let total = 0;
let passed = 0;
for (const s of scores) {
total += s;
if (s >= 60) {
passed++;
}
}
return {
average: (total / scores.length).toFixed(1),
max: Math.max(...scores),
passed,
};
};
const classA = [82, 45, 91, 68];
const classB = [70, 55, 88];
for (const [name, scores] of [["A組", classA], ["B組", classB]]) {
const { average, max, passed } = summarize(scores);
console.log(`${name} → 平均${average}点 / 最高${max}点 / 合格${passed}人`);
}
同じ関数を違うデータに何度でも使えるのが関数化の最大の利点。クラスが10個に増えてもコードは1行も増えない。passed, のように変数名とキー名が同じなら省略記法が使える点も覚えておきたい。
5仕上げ課題
const items = [
{ name: "ノートPC", price: 128000, qty: 1 },
{ name: "マウス", price: 3200, qty: 2 },
{ name: "USBメモリ", price: 1500, qty: 3 },
];
const isMember = true;
定義する関数(すべてアロー関数で):
① subtotal(item) … 明細1件の小計(単価×数量)を返す
② applyDiscount(amount, isMember) … 会員なら10%引き(Math.round)、そうでなければそのまま返す
③ withTax(amount, rate = 0.1) … 税込額(Math.round)を返す。税率は省略可
処理:明細を1行ずつ出力しながら小計を積み上げ、最後に「小計 → 会員割引後 → 消費税込」の順に出力する。
出力仕様(8行):
=== 請求書 ===
ノートPC:128,000円 × 1 = 128,000円
マウス:3,200円 × 2 = 6,400円
USBメモリ:1,500円 × 3 = 4,500円
---
小計:138,900円
会員割引後:125,010円
消費税込:137,511円
出力8行が完全一致
関数を3つ先に定義してから処理を書く。ループの中で subtotal(item) を呼んで total に足し込み、ループ後に applyDiscount() → withTax() の順に通す。明細は分割代入 { name, price, qty } で取り出すと読みやすい。
const items = [
{ name: "ノートPC", price: 128000, qty: 1 },
{ name: "マウス", price: 3200, qty: 2 },
{ name: "USBメモリ", price: 1500, qty: 3 },
];
const isMember = true;
const subtotal = item => item.price * item.qty;
const applyDiscount = (amount, isMember) =>
isMember ? Math.round(amount * 0.9) : amount;
const withTax = (amount, rate = 0.1) => Math.round(amount * (1 + rate));
console.log("=== 請求書 ===");
let total = 0;
for (const item of items) {
const line = subtotal(item);
total += line;
const { name, price, qty } = item;
console.log(`${name}:${price.toLocaleString()}円 × ${qty} = ${line.toLocaleString()}円`);
}
const discounted = applyDiscount(total, isMember);
const taxed = withTax(discounted);
console.log("---");
console.log(`小計:${total.toLocaleString()}円`);
console.log(`会員割引後:${discounted.toLocaleString()}円`);
console.log(`消費税込:${taxed.toLocaleString()}円`);
小さな関数を並べて、処理の流れで組み立てる——これが実務のプログラムの標準的な形。税率が変わっても withTax の中だけ、割引率が変わっても applyDiscount の中だけを直せばよい。変更が1箇所で済む構造を作れることが、関数を学ぶ本当の目的。
また ① の subtotal(item) はオブジェクトをまるごと受け取っている点に注目してほしい。subtotal(price, qty) と2つ渡す設計もあり得るが、明細の項目が増えたときに引数が際限なく増える。関連するデータはオブジェクトのまま渡すのが拡張に強い。
次章では map / filter / reduce を学ぶ。ここで書いた for ループが、アロー関数を使って1行に凝縮される。