関数
処理の共通化
数学における関数は、数と数の関係のようなものですが、JavaScript をはじめとしたプログラミング言語の文脈における関数は、基本的には文のまとまりに名前を付けたものです。
// 関数を定義しておけば
function greet() {
document.write("Hello World!");
}
// 後から呼び出すことができる
greet();
greet();
上のプログラムにおいて、function
キーワードから始まる部分は関数を定義するための制御構文です。関数定義では、 function
キーワードに続けて関数名、かっこを記述します。この後、関数内で実行したい処理を波かっ この中に記述していきます。
関数を定義すると、関数名に続けてかっこを記述することにより、その関数を実行できるようになります。
このプログラムでは、greet
関数が 2 回呼び出されているので、ブラウザに Hello World!
が 2 つ表示されます。
引数
関数の振る舞いを呼び出し時に変更するため、関数に引数を与えることができます。 引数には任意の値が指定できます。
function greet(greetingType, myName) {
document.write("Good " + greetingType + ", " + myName + "!");
}
greet("morning", "佐藤");
関数定義では、関数名直後のかっこ内に引数名をコンマ区切りで設定できます。
上のプログラムで greet
関数は、greetingType
や myName
という名前の引数をとります。
関数定義の中では、これらは変数のように振舞います。
呼び出し側では、括弧の中に関数に渡す引数を指定します。このプログラムを実行すると、ブラウザに Good morning, 佐藤!
が表示されるでしょう。
戻り値
関数呼び出しは式の一種です。 関数定義内で return 文 を用いると、関数の実行が停止され、関数呼び出し式の評価結果が確定します。 この値を戻り値と呼びます。 ある値を戻り値として設定して関数の実行を終了することを、関数がその値を返すと表現します。
function add(a, b) {
const sum = a + b;
return sum;
}
document.write(add(3, 4));
上の例の 6 行目で、式 add(3, 4)
が評価されると、a = 3, b = 4
として add
関数が実行されます。add
関数の中で文 const sum = a + b;
が実行されると、式 a + b
が評価され、7
になります。これにより、sum
に 7
が代入されます。
次の行 return sum;
で add
関数は変数 sum
を評価した結果である、7
を返します。
そして式 add(3, 4)
の評価結果が 7
となります。
return 文 が実行された時点で関数の処理が終了するため、次のように書くことで if 〜 else 文 や && (AND) 演算子の繰り返しを避けつつ、複数の条件のついた処理を実行することができます。
let age = 21;
let hasDriverLicense = true;
let isDrunk = true;
function tryToDrive() {
// if 文で実行する式が一行だけの場合、{} を省略できます。
if (age < 18) return;
if (!hasDriverLicense) return;
if (isDrunk) return;
document.write("車を運転できます。");
}
確認問題
引数を 2 つとり、その積を戻り値として返す関数 multiply
を定義してください。
解答例: 2つの積
function multiply(a, b) {
const result = a * b;
return result;
}
document.write(multiply(3, 4));
変数のスコープ
関数内で宣言された変数は、関数内でのみ有効です。 変数が有効な範囲のことを、その変数のスコープと呼んでいます。
関数外で宣言された変数は関数内でも利用できます。
let guestCount = 0;
function greet() {
guestCount += 1;
document.write("あなたは" + guestCount + "人目の お客様です。");
}
greet(); // あなたは1人目のお客様です。
greet(); // あなたは2人目のお客様です。
この例における、greet
関数は、呼び出されるたびに guestCount
に 1 を加えています。
複合代入演算子 は、計算と代入を同時に行うことができる演算子です。
x += y
は、x = x + y
という意味になります。他にも -=
や *=
などの演算子が定義されています。x -= y
は x = x - y
、x *= y
は x = x * y
という意味になります。
guestCount += 1;
は以下の文のように読み替えられます。
guestCount = guestCount + 1;
スコープが終わった変数は、その時点で破棄されます。
let outer = 0;
function increment() {
let inner = 0;
outer += 1;
inner += 1;
document.write(outer); // 1ずつ増える
document.write(inner); // 常に1
}
increment();
increment();
処理の分割
関数は、複数回使用する処理を簡便に記述するためだけでなく、複雑で長い処理の一部を切り出すことにも用いることができます。
長い処理をパーツに分割すると、次のようなメリットがあります。
- 考えるべきことが少なくなるので、読みやすい
- パーツごとにテストができるので、デバッグがしやすい
- パーツの使いまわしができる
次の例は、totalTicketCount
枚のうち hitTicketCount
枚の当たりがあるくじから drawnTicketCount
枚を引いたときに、当たりが少なくとも 1 枚出る確率 winningProbability
を、■■■□□□□□□□
のようなグラフ形式で表示するプログラムです。
const totalTicketCount = 10;
const hitTicketCount = 5;
const drawnTicketCount = 3;
let losingProbability = 1;
for (let i = 0; i < drawnTicketCount; i += 1) {
losingProbability *=
(totalTicketCount - hitTicketCount - i) / (totalTicketCount - i);
}
const winningProbability = 1 - losingProbability;
for (let p = 0; p < 1; p += 0.1) {
if (p < winningProbability) {
document.write("■");
} else {
document.write("□");
}
}
このプログラムを、関数を用いて確率を計算する部分とグラフとして表示する部分に分割すると、次のようになります。先頭の 9 行を読むだけでプログラム全体の処理の流れが追えるようになりました。
const totalTicketCount = 10;
const hitTicketCount = 5;
const drawnTicketCount = 3;
const winningProbability = calculateWinningProbability(
totalTicketCount,
hitTicketCount,
drawnTicketCount,
);
showProbabilityAsGraph(winningProbability);
function calculateWinningProbability(
totalTicketCount,
hitTicketCount,
drawnTicketCount,
) {
let losingProbability = 1;
for (let i = 0; i < drawnTicketCount; i += 1) {
losingProbability *=
(totalTicketCount - hitTicketCount - i) / (totalTicketCount - i);
}
return 1 - losingProbability;
}
function showProbabilityAsGraph(probability) {
for (let p = 0; p < 1; p += 0.1) {
if (p < probability) {
document.write("■");
} else {
document.write("□");
}
}
}
このように、大きなプログラムを意味的に独立した小さなまとまりに分割する操作を、モジュール化と呼ぶ場合があります。
基礎演習
最大値
引数を 2 つとり、そのうち大きい数を返す関数 max
を定義してください。
if 文を使って、a
が大きい場合と b
が大きい場合で処理を書き分けます。
解答例: 大きい数
function max(a, b) {
if (a > b) {
return a;
} else {
return b;
}
}
a > b
が true
の場合、if 文内部の return
で関数実行が中断されるため、else
キーワードは必ずしも必要ではありません。そのため、次のように書くこともできます。
function max(a, b) {
if (a > b) {
return a;
}
return b;
}
中級演習
携帯電話料金
携帯電話料金を計算する関数を作ってみましょう。
function calculateCost(monthlyDataUsage) {
// ここに処理を書く
}
document.write(calculateCost(3.5));
calculateCost
は、引数に月間転送量 monthlyDataUsage
を取り、その月の携帯電話料金を戻り値として返す関数です。携帯電話料金は、下のルールで決定されるとします。
- 月間転送量 < 5.0 (GB) のとき、携帯電話料金は 月間転送量 × 600 (円 / GB)
- 月間転送量 >= 5.0 (GB) のとき、携帯電話料金は 3000 (円)
解答例: 携帯電話料金
function calculateCost(monthlyDataUsage) {
if (monthlyDataUsage < 5.0) {
return monthlyDataUsage * 600;
}
return 3000;
}
document.write(calculateCost(3.5));