JavaScriptの関数:定義と呼び出し

2025-07-28

はじめに

JavaScriptの関数は、一連の処理をまとめて名前を付けた「再利用できる命令のかたまり」です。引数を受け取って処理し、結果を返すことができます。コードの重複を減らし、プログラムを整理するために使います。

関数の基本概念

関数はJavaScriptにおける重要な構成要素で、特定のタスクを実行するコードブロックです。関数を使うことで、コードの再利用性、可読性、保守性が向上します。

関数の利点

  • コードの再利用: 同じ処理を何度も書かなくて済む
  • モジュール化: プログラムを小さな機能単位に分割できる
  • 保守性向上: 変更が必要な場合、1箇所を修正するだけで済む

関数の定義方法

関数を使うためには、まず関数を定義(作成)し、その後呼び出して実行する手順が必要です。

1. 関数宣言(Function Declaration)

JavaScriptで最も初歩的な関数宣言は、関数宣言(function宣言)と呼ばれる書き方です。

// 関数宣言
function greet(name) {
  return `こんにちは、${name}さん!`;
}

// 呼び出し
console.log(greet('太郎')); // こんにちは、太郎さん!

特徴:

  • ホイスティングされる(定義前に呼び出せる)
  • ブロックスコープではなく関数スコープ

ホイスティングについては以前の記事を参照ください。

2. 関数式(Function Expression)

関数式は、関数を変数に代入する方法で、関数宣言とは違い「変数名を使って関数を呼び出す」形になります。

// 関数式
const greet = function(name) {  // greet変数に関数を代入する
  return `こんにちは、${name}さん!`;
};

// 呼び出し
console.log(greet('花子')); // こんにちは、花子さん!

特徴:

  • 変数に代入される
  • ホイスティングされない
  • 無名関数(匿名関数)としても使用可能

ホイスティングについては以前の記事を参照ください。

3. アロー関数(Arrow Function)ES6

無名関数は「一時的に使いたい処理」や「イベント処理、配列の操作時のコールバック」として便利で、コードを簡潔にできます。

※コールバックについては後ほど説明します。

// アロー関数
const greet = (name) => {
  return `こんにちは、${name}さん!`;
};

// 簡潔な構文(単一式の場合)
const square = x => x * x;

特徴:

  • thisのバインドが異なる(後述)
  • 簡潔な構文
  • コンストラクタとして使用できない

アロー関数については別途詳しく説明してます。

関数の呼び出し

JavaScriptで関数を使うには、定義した関数名の後に丸括弧 () を付けて呼び出します。このとき、必要に応じて丸括弧の中に引数を渡すことができます。

基本的な呼び出し

定義した関数名の後に丸括弧 () を付けて呼び出します。

function add(a, b) {
  return a + b;
}

const result = add(3, 5); // 8

メソッドとしての呼び出し

変数に格納された関数を呼び出すとは、関数を代入した変数名に続けて括弧 () を記述します。

const calculator = {
  add: function(a, b) {
    return a + b;
  }
};

calculator.add(2, 3); // 5

コンストラクタとしての呼び出し

new演算子と組み合わせて使用され、新しいオブジェクトを生成し、初期化する役割を担います。

function Person(name) {
  this.name = name;
}

const person = new Person('太郎');

間接的な呼び出し

関数名の後に直接 () を付けて実行するのではなく、関数を別の変数に代入したり、他の関数の引数として渡したりして、あとから呼び出す方法です。

function greet() {
  console.log(`こんにちは、${this.name}さん!`);
}

const user = { name: '花子' };
greet.call(user); // こんにちは、花子さん!

パラメータと引数

パラメータ:関数を定義するときに、処理のために受け取る変数の名前のこと。

引数:関数を呼び出すときに、パラメータに渡す具体的な値のこと。

デフォルトパラメータ(ES6)

デフォルトパラメータは、関数のパラメータに初期値を設定できるES6の機能です。呼び出し時に引数が渡されなかったり、undefinedの場合に、あらかじめ指定した値が使われます。

function greet(name = 'ゲスト') {
  console.log(`こんにちは、${name}さん!`);
}

greet(); // こんにちは、ゲストさん!
greet('太郎'); // こんにちは、太郎さん!

残余パラメータ(Rest Parameters)

残余パラメータ(Rest Parameters)は、関数の引数をまとめて配列として受け取るES6の機能です。関数が不特定多数の引数を扱いたいときに便利です。

function sum(...numbers) {
  return numbers.reduce((acc, num) => acc + num, 0);
}

console.log(sum(1, 2, 3)); // 6
console.log(sum(1, 2, 3, 4, 5)); // 15

引数の分割代入

引数の分割代入とは、関数の引数としてオブジェクトや配列を受け取り、その中の値を直接変数に取り出す書き方です。コードがシンプルで見やすくなります。

function printUser({ name, age }) {
  console.log(`${name}さんは${age}歳です`);
}

const user = { name: '太郎', age: 25 };
printUser(user); // 太郎さんは25歳です

戻り値とスコープ

戻り値(return値):関数が処理結果を呼び出し元に返す値のこと

スコープ:変数や関数が「見える(アクセスできる)」範囲のこと

戻り値の重要性

戻り値(return値)は、関数が処理結果を呼び出し元に返す値のことです。returnキーワードで指定し、戻り値があることで関数の結果を他の処理に使えます。

// 戻り値がある関数
function add(a, b) {
  return a + b;
}

// 戻り値がない関数(undefinedを返す)
function noReturn() {
  // 何も返さない
}

スコープの挙動

スコープは、変数や関数が「見える(アクセスできる)」範囲のことです。関数内で宣言した変数はその関数内だけ有効(ローカルスコープ)で、外からは見えません。グローバルスコープはプログラム全体で有効な範囲です。

let globalVar = 'グローバル';

function testScope() {
  let localVar = 'ローカル';
  console.log(globalVar); // アクセス可能
  console.log(localVar); // アクセス可能
}

testScope();
console.log(globalVar); // アクセス可能
// console.log(localVar); // エラー(アクセス不可)

スコープについては別記事でより詳しく説明しています。

応用関数

高階関数(Higher-Order Function)

高階関数(Higher-Order Function)とは、関数を引数に取ったり、関数を返したりする関数のことです。処理を柔軟に組み合わせるのに使います。

// 関数を引数に取る関数
function operate(a, b, operation) {
  return operation(a, b);
}

function add(x, y) { return x + y; }
function multiply(x, y) { return x * y; }

console.log(operate(3, 4, add)); // 7
console.log(operate(3, 4, multiply)); // 12

コールバック関数

コールバック関数(Callback Function)は、他の関数に渡されて実行される関数のことです。非同期処理やイベント処理でよく使われます。

// コールバックを使用した非同期処理の模倣
function fetchData(callback) {
  setTimeout(() => {
    callback('データ');
  }, 1000);
}

fetchData(data => {
  console.log('取得したデータ:', data);
});

コールバックとは、ある関数を呼び出す際に、別の関数を引数として渡し、その引数として渡された関数を、元の関数の中で後で実行させる仕組みのことです。

即時実行関数式(IIFE)

即時実行関数式(IIFE: Immediately Invoked Function Expression)は、関数を定義すると同時にすぐ実行する仕組みです。

  • 一度だけ実行される
  • 変数のスコープを限定してグローバル汚染を防ぐ
  • 古いJavaScriptでもモジュールのように使える
// 定義と同時に実行
(function() {
  console.log('即時実行');
})();

// スコープを隔離する用途で使用
(function() {
  let privateVar = '外部からアクセス不可';
  // ここでの変数は外部から見えない
})();

再帰関数

再帰関数とは、自分自身を呼び出す関数のことです。繰り返し処理や階層的なデータ処理に使われます。ポイントは「終了条件(ベースケース)」を必ず設けて、無限に呼び出され続けないようにすることです。

// 階乗を計算する再帰関数
function factorial(n) {
  if (n <= 1) return 1;
  return n * factorial(n - 1);
}

console.log(factorial(5)); // 120

関数のベストプラクティス

  1. 単一責任の原則: 1つの関数は1つのことだけを行う
  2. 説明的な名前: 関数名から機能がわかるように
  3. 適切な長さ: 一般的に20行以内が目安
  4. 副作用を最小限に: 外部状態を変更しない純粋関数を目指す
  5. デフォルト引数を使用: 未定義エラーを防ぐ
  6. 適切なコメント: 複雑なロジックには説明を追加

まとめ

関数はプログラムの基本単位で、繰り返し使える便利な仕組み。目的ごとに処理を分けて書くことで、読みやすく修正しやすいコードになります。関数を覚えることはJavaScriptの理解に欠かせません。

演習問題

初級問題(3問)

  1. 次の関数を関数式とアロー関数で書き換えなさい。
   function multiply(a, b) {
     return a * b;
   }
  1. 次の関数呼び出しの出力結果は?
   function sayHello(name = 'ゲスト') {
     console.log(`こんにちは、${name}さん!`);
   }

   sayHello();
   sayHello('太郎');
  1. 次の関数の間違いを指摘し、修正しなさい。
   function sum(a, b) {
     console.log(a + b);
   }

   const result = sum(2, 3);
   console.log(result);

中級問題(6問)

  1. 次の関数を残余パラメータを使用して書き換えなさい。
   function sum(a, b, c) {
     return a + b + c;
   }
  1. 次のコードの出力結果とその理由を説明しなさい。
   let x = 10;

   function updateX() {
     x = 20;
   }

   updateX();
   console.log(x);
  1. 引数として受け取った数値が 正の数か負の数かを判定する関数 isPositive を作成しなさい。ゼロの場合は "zero"、正の数は "positive"、負の数は "negative" を返すようにしてください。
  2. 次の高階関数を使用して、配列の各要素を2倍にするコードを書きなさい。
   function mapArray(arr, transform) {
     const result = [];
     for (let item of arr) {
       result.push(transform(item));
     }
     return result;
   }
  1. オブジェクトを受け取り、そのプロパティをすべて表示する関数printObjectを作成しなさい。
  2. 次の再帰関数の動作を説明し、factorial(4)の呼び出し時の実行フローを追跡しなさい。
   function factorial(n) {
     if (n <= 1) return 1;
     return n * factorial(n - 1);
   }

上級問題(3問)

  1. カリー化された関数addを作成しなさい。以下のように動作するものとします。
console.log(add(2)(3)(4)()); // 9 
console.log(add(1)(2)()); // 3
  1. メモ化を実装したフィボナッチ関数を作成しなさい。再帰計算の効率を向上させること。
  1. 以下の要件を満たす関数createCounterを実装しなさい。

  • 呼び出すたびに1ずつ増加するカウンタを返す
  • 複数の独立したカウンタを作成可能
  • 初期値と増分値を指定可能

const counter1 = createCounter(); 
console.log(counter1()); // 1 
console.log(counter1()); // 2 
const counter2 = createCounter(10, 5); 
console.log(counter2()); // 15 
console.log(counter2()); // 20

解答例

初級問題解答

  1. 関数式とアロー関数で書き換え
   // 関数式
   const multiply = function(a, b) {
     return a * b;
   };

   // アロー関数
   const multiply = (a, b) => a * b;
  1. 呼び出しの出力結果
   こんにちは、ゲストさん!
   こんにちは、太郎さん!
    function sayHello(name = 'ゲスト') {
        console.log(`こんにちは、${name}さん!`);
    }

    sayHello();         // こんにちは、ゲストさん!
    sayHello('太郎');    // こんにちは、太郎さん!
  1. 間違いと修正
   // 間違い: console.logで出力しているだけで値を返していない
   // 修正例:
   function sum(a, b) {
     return a + b;
   }

中級問題解答

  1. 残余パラメータを使用して書き換え
   function sum(...numbers) {
     return numbers.reduce((acc, num) => acc + num, 0);
   }
  1. コードの出力結果とその理由
   20
    let x = 10;

    function updateX() {
        x = 20;     // 宣言してないのでグローバル変数として認識される
    }

    updateX();      // 関数内でグローバル変数xを更新しているため
    console.log(x);
  1. 正の数か負の数かを判定する関数 isPositive
    function isPositive(num) {
    if (num > 0) {
        return "positive";
    } else if (num < 0) {
        return "negative";
    } else {
        return "zero";
    }
    }
  1. 高階関数を使用して、配列の各要素を2倍にするコード
   const doubled = mapArray([1, 2, 3], x => x * 2);
   // [2, 4, 6]
  1. プロパティをすべて表示する関数printObject
   function printObject(obj) {
     for (const key in obj) {
       console.log(`${key}: ${obj[key]}`);
     }
   }
  1. factorial(4)の実行フロー
   factorial(4)
   → 4 * factorial(3)
     → 3 * factorial(2)
       → 2 * factorial(1)
         → 1 (ベースケース)
       → 2 * 1 = 2
     → 3 * 2 = 6
   → 4 * 6 = 24

上級問題解答

  1. カリー化された関数add
function add(a) { 
    return function(b) { 
         return function(c) { 
             return function() { 
                 return a + b + c;
             };
         };
    };
}

関数のカリー化とは、一回性で必要な引数を入れるのではなく、あらかじめ関数の引数を数段階に分けて、段階別に引数を自由に操作できる構造です。

  1. メモ化を実装したフィボナッチ関数
function fibonacci(n, memo = {}) { 
    if (n in memo) return 
    memo[n]; 
    if (n <= 1) return n; 
    memo[n] = fibonacci(n - 1, memo) + fibonacci(n - 2, memo); 
    return memo[n]; 
}
  1. createCounter関数:
javascript function createCounter(initial = 0, step = 1) {
    let count = initial;
    return function() {
        count += step;
        return count;
    };
}