JavaScriptの関数について
関数の基本、無名関数、アロー関数、スコープなど
今回のゴール
- 様々な関数の書き方、関数に関する基礎知識を理解する
関数の呼び出し
関数を呼び出すには、関数名の後に括弧 () をつけます。
// 関数の定義
function sayHello() {
console.log("こんにちは!");
}
// 関数の呼び出し
sayHello(); // "こんにちは!" と表示される
sayHello; // 効果なし
() をつけないと、関数は実行されず、関数そのものを参照するだけになります。
逆に、関数そのものを渡す必要がある場合は () は不要です
button.addEventListener("click", handleClick);
// () をつけると、関数が実行された結果が渡されるのでNG
button.addEventListener("click", handleClick()); // これは間違い
関数の引数
関数を呼び出すときに、括弧の中に値を入れ、関数の中で変数として使えます。これを引数と呼びます。
引数を使うと、柔軟で再利用しやすい関数を作れます。
// 引数なしの場合
function showTaxPrice() {
const price = 1000;
console.log(`税込価格: ${price * 1.1}円`);
}
// 引数ありの場合(柔軟に使える)
function showTaxPriceFor(price) {
console.log(`税込価格: ${price * 1.1}円`);
}
showTaxPriceFor(500); // "税込価格: 550円"
showTaxPriceFor(1200); // "税込価格: 1320円"
引数が複数の場合
関数は複数の引数を受け取ることができます。
引数はカンマ(,)で区切って指定します。
// 2つの引数を受け取る関数
function introduce(name, age) {
console.log(`私は${name}、${age}歳です。`);
}
introduce("山田", 25); // "私は山田、25歳です。"
introduce("佐藤", 30); // "私は佐藤、30歳です。"
引数を渡す順番は、関数で定義された順番と一致させる必要があります。
オプション引数
関数が持つ引数の中には、省略できるもの (オプション引数) もあります。
オプション引数を省略した場合、引数は既定の値 (デフォルト値) として取り扱われます。
// 文字列の slice() メソッドの例
const text = "JavaScript";
// 引数を2つ指定した場合(開始位置と終了位置)
console.log(text.slice(0, 4)); // "Java"
// 引数を1つだけ指定した場合(開始位置のみ、最後まで取得)
console.log(text.slice(4)); // "Script"
オプション引数を作る
自分で関数を作るとき、引数のデフォルト値を割り当てて、オプションの引数とすることができます。
以下のように、関数の定義において 引数名 = デフォルト値 のように記載します。
// greeting にデフォルト値を設定
function greet(name, greeting = "こんにちは") {
console.log(`${greeting}、${name}さん!`);
}
greet("田中", "おはよう"); // "おはよう、田中さん!"
greet("鈴木"); // "こんにちは、鈴木さん!"
返り値とは
返り値(戻り値)とは、関数が処理の結果として返す値のことです。
return キーワードを使って値を返します。
function add(a, b) {
return a + b; // 計算結果を返す
}
const result = add(3, 5);
console.log(result); // 8
返り値は、変数に代入したり、別の処理に使ったりできます。
関数を返す関数
関数の中に関数を定義し、関数の返り値として関数を返すこともできます。
function createMultiplier(multiplier) {
// 関数を返す
function multiply(number) {
return number * multiplier;
}
return multiply;
}
// 2倍する関数を作成
const double = createMultiplier(2);
console.log(double(5)); // 10
// 3倍する関数を作成
const triple = createMultiplier(3);
console.log(triple(5)); // 15
返り値がない関数
関数に return がない場合に関数の結果を取得すると、undefined となります。
function showMessage(text) {
console.log(text); // 表示するだけで return なし
}
const result = showMessage("こんにちは");
console.log(result); // undefined
return; で処理を終了する
return; だけを書くと、関数の処理を途中で終了できます (関数の結果として undefined が返ります)。
forEach の中で使うと、ループ処理の continue のように使えます。
const numbers = [1, 2, 3, 4];
numbers.forEach(function (num) {
if (num === 3) {
return; // この要素の処理を終了(次の要素の処理は続く)
}
console.log(num);
});
// 出力:
// 1
// 2
// 4
// (3は表示されない)
early return パターン
条件が満たされない場合に早期に return することで、 メインの処理が if の括弧に囲まれるのを防ぎ、コードを読みやすくするパターンなどにも利用できます。
function processOrder(order) {
if (order !== null) {
if (order.items.length > 0) {
// 長い処理...
}
}
}
// early return を使う場合
function processOrder(order) {
if (order === null) {
return; // 早期に終了
}
if (order.items.length === 0) {
return; // 早期に終了
}
// 処理の続き...
}
無名関数
これまでの関数には名前がついていましたが、名前を持たない無名関数も作れます。
// 通常の関数(名前あり)
function greet() {
console.log("こんにちは");
}
// 無名関数(名前なし)
(function () {
console.log("こんにちは");
});
無名関数の例
無名関数は、他の関数に引数として渡す場合によく使われます。
const button = document.querySelector("button");
// 無名関数をイベントリスナーに渡す
button.addEventListener("click", function () {
console.log("ボタンがクリックされました!");
});
// 無名関数を map メソッドに渡す
const prices = [100, 200, 300];
const pricesWithTax = prices.map(function (price) {
return price * 1.1; // 消費税10%を加算
});
console.log(pricesWithTax); // [110, 220, 330]
アロー関数 (1)
アロー関数は、無名関数をより短く書ける構文です。
function の代わりに => (矢印)を使います。
// アロー関数をイベントリスナーに渡す
button.addEventListener("click", () => {
console.log("ボタンがクリックされました!");
});
// アロー関数を map メソッドに渡す
const prices = [100, 200, 300];
const pricesWithTax = prices.map((price) => {
return price * 1.1; // 消費税10%を加算
});
console.log(pricesWithTax); // [110, 220, 330]
アロー関数(2)- 省略記法
アロー関数は、以下のようにさらに短く書ける省略記法があります。
- 引数が一つの場合、引数の
() を省略できます
- 一行で値を返す処理であれば、
{} と return が省略できます
(省略記法を使わない場合)
const prices = [100, 200, 300];
const pricesWithTax = prices.map((price) => {
return price * 1.1; // 消費税10%を加算
});
(省略記法を使う場合)
const prices = [100, 200, 300];
const pricesWithTax = prices.map(price => price * 1.1);
変数に無名関数・アロー関数を代入する
関数を変数に代入して使うこともでき、これを関数式と呼びます。
関数が代入された変数は、関数名のように使えます。
// 無名関数を変数に代入
const add = function (a, b) {
return a + b;
};
// アロー関数を変数に代入
const subtract = (a, b) => a - b;
console.log(add(10, 3)); // 13
console.log(subtract(10, 3)); // 7
関数宣言と関数式の違い
関数宣言と関数式は似ていますが、関数宣言は関数の宣言よりも前に関数が呼び出せる ("巻き上げ" と呼ばれる) という特徴があります。
// これはOK (関数宣言の巻き上げ)
greet1();
function greet1() {
console.log("こんにちは");
}
// 関数式の場合はNG
greet2();
const greet2 = () => {
console.log("こんにちは");
};
シンプルさからアロー関数を使った関数式が好まれる場合が多いですが、明確にどちらが良いということはないため、プロジェクトで統一されていれば良いでしょう。
関数のスコープ(1)
スコープとは、変数が有効な範囲のことです。
関数の中で宣言した変数は、関数のスコープに属するものとなり、関数の外からはアクセスできません。
ローカル変数とも呼ばれます。
function showMessage() {
const message = "関数の中で宣言";
console.log(message);
}
showMessage();
console.log(message); // エラー! message は関数の外では使えない
関数のスコープ(2)
関数の外で宣言した変数は、グローバルスコープに属し、どこからでもアクセスできるようになります。
このような変数を グローバル変数 と呼びます。
const globalMessage = "グローバル変数";
function showMessage() {
console.log(globalMessage); // OK(グローバル変数にアクセス)
}
showMessage();
console.log(globalMessage); // OK
グローバル変数は便利ですが、様々な箇所で参照・更新されて管理が難しくなることなどから、基本的に推奨されません。
関数のスコープ(3)- なぜスコープが必要か
スコープがあることで、変数名の衝突を防げます。
function calcTax() {
const rate = 0.1; // 関数内の rate
return 1000 * rate;
}
function calcDiscount() {
const rate = 0.2; // こちらも rate だが、別の変数
return 1000 * rate;
}
console.log(calcTax()); // 100(税率10%)
console.log(calcDiscount()); // 200(割引率20%)
それぞれの関数内で rate という同じ名前を使っても問題ありません。
内側の関数は外側の関数のスコープにアクセスできる
関数の中に関数を作ると、内側の関数は外側のスコープにある変数にアクセスできます (逆に、外側の関数から内側の関数の変数にアクセスすることはできません)
function calculateTotalPrices(prices) {
const taxRate = 0.1; // 外側の関数の変数
function calculateTax(price) {
// 内側の関数は外側の関数の変数を使える
return price * taxRate;
}
return prices.map(calculateTax);
}
const totalPrices = calculateTotalPrices([100, 200, 1000]);
関数を使って複雑なコードをわかりやすくする
関数は、繰り返し使う処理を何回も書くことを防げますが、それだけではありません。意味のある処理のかたまりを関数として分けることで、コード全体を見やすくすることができます。 以下のような点を意識しましょう。
- 全体の中で独立した意味を持つ処理のかたまりを見つけ、関数として分離する
- どういう入力・出力となる処理かがすぐに理解できるような名前をつける
関数を使って複雑なコードをわかりやすくする (例)
例えば、以下のような処理を見てみましょう。関数を使って読みやすくできるでしょうか?
const resultElement = document.getElementById('result');
const ageInput = document.getElementById('age');
const age = parseInt(ageInput.value, 10);
if (!Number.isNaN(age) && age >= 0) {
resultElement.textContent = '正しい年齢です';
console.log('処理が成功しました');
} else {
resultElement.textContent = '年齢が不正です';
}
前のページの例は、以下のように2つの関数を使うことで、処理の意味を理解しやすくできます。
function isValidAge() {
const ageInput = document.getElementById('age');
const age = parseInt(ageInput.value, 10);
return (!Number.isNaN(age) && age >= 0);
}
function showResult(text) {
const resultElement = document.getElementById('result');
resultElement.textContent = text;
}
if (isValidAge()) {
showResult('正しい年齢です');
console.log('処理が成功しました');
} else {
showResult('年齢が不正です');
}
まとめ
- 関数名の後に
() をつけて関数を呼び出す。関数自体を使う場合は () は不要
- 関数は引数を受け取ることができ、複数の場合はカンマで区切る
- オプション引数で引数が省略されたときの既定値を設定できる
- 関数は
return で値を返すことができ、早期に return; することで処理をすぐ終了することもできる
- 無名関数は名前を持たない関数で、他の関数に引数として渡す時に便利
- アロー関数は
=> を使った短い関数の書き方で、返り値などの省略記法もある
- 関数式は関数を変数に代入する書き方で、関数宣言との違いは巻き上げの有無
- スコープは変数が有効な範囲で、関数内の変数は外から見えない
- 関数を適切に使うことで全体の処理をわかりやすくできる
さらに学ぶには