Skip to content
Playground

6.関数

学習目標

  • 処理を関数にまとめて定義し、呼び出せる
  • 引数で値を受け取り、戻り値で結果を返す関数を書ける

配列とループ: 最大値の探索で、配列から最大値を求めるプログラムを書きました。別の配列に対しても同じ処理をしたい場合、次のように同じコードを書き足すことになります。

functions.js
// 1つ目の配列から最大値を求める
const numbers = [34, 72, 15, 88, 43];
let max1 = numbers[0];
for (let i = 1; i < numbers.length; i++) {
if (numbers[i] > max1) {
max1 = numbers[i];
}
}
console.log(max1);
// 2つ目の配列から最大値を求める
const scores = [80, 65, 90, 70, 85];
let max2 = scores[0];
for (let i = 1; i < scores.length; i++) {
if (scores[i] > max2) {
max2 = scores[i];
}
}
console.log(max2);
88
90

同じコードが2回並んでいます。配列の数が3つ、4つと増えれば、その分だけ同じコードを書く必要があります。こうした重複をなくすために、処理に名前をつけて1か所にまとめ、何度でも実行できる仕組みを 関数 と呼びます。

function キーワードを使って、処理に名前をつけて関数として定義します。

function 関数名() {
実行する処理
}
functions.js
function greet() {
console.log("こんにちは");
}
greet();
greet();
こんにちは
こんにちは

function greet() { ... } で、console.log("こんにちは") という処理に greet という名前をつけて関数として定義します。

greet() と書くと、定義した処理が実行されます。これを 関数の呼び出し と言います。greet() を2回書いているので、こんにちは が2回出力されます。

次のコードで、関数を含むプログラムの実行順序を確認します。

functions.js
console.log("1: 開始");
function greet() {
console.log("2: 関数の中");
}
console.log("3: 呼び出し前");
greet();
console.log("4: 呼び出し後");
1: 開始
3: 呼び出し前
2: 関数の中
4: 呼び出し後

実行の流れを追います。

  1. console.log("1: 開始") が実行され、1: 開始 が出力される
  2. function greet() { ... } は関数の定義なので、greet 関数の中は実行されない
  3. console.log("3: 呼び出し前") が実行され、3: 呼び出し前 が出力される
  4. greet() で関数が呼び出され、greet 関数の中の console.log("2: 関数の中") が実行されて 2: 関数の中 が出力される
  5. 関数の処理が終わると、greet() の次の行に戻り、console.log("4: 呼び出し後") が実行されて 4: 呼び出し後 が出力される

先ほどの greet 関数は毎回同じ挨拶しかできません。相手の名前に応じてメッセージを変えたい場合はどうすればよいでしょうか。

関数定義の () の中に 引数 と呼ばれる変数を書いておくと、関数を呼び出すときにその変数へ値を渡せます。

function 関数名(引数) {
実行する処理
}
functions.js
function greet(name) {
console.log(name + "さん、こんにちは");
}
greet("田中");
greet("鈴木");
田中さん、こんにちは
鈴木さん、こんにちは

function greet(name)name が引数です。greet("田中") を呼び出したときの流れを追います。

  1. "田中" が引数 name に渡される
  2. 関数の中の name + "さん、こんにちは" が評価され、"田中さん、こんにちは" となる
  3. 田中さん、こんにちは が出力される

greet("鈴木") の場合も同じ仕組みで、鈴木さん、こんにちは が出力されます。

引数はカンマで区切って複数書くことができます。

functions.js
function add(a, b) {
console.log(a + b);
}
add(10, 20);
add(3, 7);
30
10

複数の引数があるとき、呼び出し時の値は書いた順番に対応します。add(10, 20) を呼び出したときの流れを追います。

  1. 値が引数に渡される
    • 1 番目の 10 → 1 番目の引数 a
    • 2 番目の 20 → 2 番目の引数 b
  2. a + b が評価され、30 となる
  3. 30 が出力される

add(3, 7) の場合も同じ仕組みで、3 + 7 が評価されて 10 が出力されます。

関数の結果を変数に保存したり別の式で使ったりしたい場合は、関数の中から呼び出し元へ値を返します。返される値を 戻り値 と呼び、return で指定します。

function 関数名(引数) {
return 値;
}
functions.js
function add(a, b) {
return a + b;
}
const result = add(10, 20);
console.log(result);
console.log(add(3, 7));
30
10

add(10, 20) を呼び出したときの流れを追います。

  1. add(10, 20) が評価され、戻り値の 30 になる
  2. 30result に代入される
  3. console.log(result)30 が出力される

console.log(add(3, 7)) のように、変数を経由せず console.log の引数として直接渡すこともできます。

関数に return があるかないかで、呼び出し元が結果を受け取れるかが決まります。同じ足し算を行う関数を return ありとなしで比べてみます。

functions.js
function add(a, b) {
console.log(a + b);
}
const x = add(10, 20);
console.log(x);
30
undefined

関数の中の console.log(a + b)30 を出力します。add には return がないので戻り値は undefined で、console.log(x)undefined を出力します。

関数の結果を呼び出し元で使いたい場合は return を書きます。

戻り値を変数に保存すれば、別の計算に活用できます。次のプログラムは、関数で小計を計算し、そこから税込みの合計を求めます。

functions.js
function calcSubtotal(price, quantity) {
return price * quantity;
}
const subtotal = calcSubtotal(500, 3);
const tax = subtotal * 0.1;
const total = subtotal + tax;
console.log(total);
1650

subtotal には 1500 が入ります。その subtotaltaxtotal の 2 つの計算で使われ、最終的に 1650 が出力されます。

return で値を返すことで、結果を subtotal に保存し、taxtotal の計算に使えます。

関数の中には return を複数書くことができます。return が実行されると関数はそこで終わるので、最初に実行された return だけが動き、残りは実行されません。この書き方を 早期リターン と呼びます。

functions.js
function checkAge(age) {
if (age < 0) {
return "不正な値です";
}
if (age >= 18) {
return "成人です";
}
return "未成年です";
}
console.log(checkAge(20));
console.log(checkAge(-1));
console.log(checkAge(15));
成人です
不正な値です
未成年です

それぞれの呼び出しでどの return が実行されるかを追います。

  • checkAge(20): age < 0false なので 1 つ目の if を通過します。age >= 18true なので return "成人です" で関数が終了します。3 つ目の return には到達しません。
  • checkAge(-1): age < 0true なので、すぐに return "不正な値です" で関数が終了します。残りの 2 つの return は実行されません。
  • checkAge(15): age < 0age >= 18false なので、2 つの if を通過して return "未成年です" が実行されます。

条件分岐: 最大値の判定で、2 つの数から大きいほうを求めるコードを書きました。同じロジックを関数にまとめると、どんな 2 つの値でも使い回せます。

functions.js
function getMax(a, b) {
let max;
if (a > b) {
max = a;
} else {
max = b;
}
return max;
}
console.log(getMax(30, 45));
console.log(getMax(100, 50));
45
100

どちらの書き方でも、getMax(30, 45) では 45 が、getMax(100, 50) では 100 が返されます。「let max を使う」形は 条件分岐: 最大値の判定 の構造を関数に包んだもので、「早期リターンを使う」形は 早期リターン のパターンを応用したものです。

配列から最大値を求める処理を関数にまとめます。

functions.js
function findMax(arr) {
let max = arr[0];
for (let i = 1; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
return max;
}
console.log(findMax([34, 72, 15, 88, 43]));
console.log(findMax([80, 65, 90, 70, 85]));
88
90

どんな配列を渡しても最大値が返ってきます。処理は1箇所にまとまっているので、ロジックを修正したいときも findMax の中だけを直せば、すべての呼び出し箇所に反映されます。

配列とループ: 合計値の計算で書いた配列の合計を求めるコードを、関数にまとめます。

functions.js
function calcSum(arr) {
let sum = 0;
for (let i = 0; i < arr.length; i++) {
sum = sum + arr[i];
}
return sum;
}
const scores = [80, 65, 90, 70, 85];
const total = calcSum(scores);
const average = total / scores.length;
console.log(total);
console.log(average);
390
78

calcSum(scores) の戻り値 390total に代入され、total / scores.length で平均 78 が計算されます。