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

2025-07-28

はじめに

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

関数の基本概念

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

例えば以下のような関数の定義と呼び出しがあります。

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

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

関数にすることで以下の利点を得ることができます。

関数の利点

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

関数の定義方法

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

関数定義の種類と特徴

定義方法構文例巻き上げ再代入thisの扱い特徴使用場面
関数宣言function add(a, b) { return a + b; }○ (可能)× (不可)呼び出し元に依存– コード内どこでも呼び出し可能
– 最も基本的な定義方法
– ブロックスコープの影響を受けない
汎用的な関数
再利用性の高い関数
関数式const add = function(a, b) { return a + b; };× (不可)△ (constの場合は不可)呼び出し元に依存– 変数に代入して定義
– 匿名関数も可能
– スコープの制御が容易
コールバック関数
即時関数(IIFE)
アロー関数const add = (a, b) => a + b;× (不可)△ (constの場合は不可)lexicalスコープを継承– 簡潔な記述
– thisを束縛しない
– コンストラクタとして使用不可
– argumentsオブジェクトなし
コールバック
メソッド内の関数
thisの固定が必要な場合

それぞれの宣言方法について詳細に見ていきます。

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

定義方法構文例巻き上げ再代入thisの扱い特徴使用場面
メソッド定義const obj = { add(a, b) { return a + b; } };× (不可)△ (オブジェクトごとなら可能)所属オブジェクト– オブジェクトのプロパティとして定義
– 簡潔なメソッド記法
– 算出プロパティ名と併用可能
オブジェクトのメソッド
クラス内メソッド
Functionコンストラクタconst add = new Function('a', 'b', 'return a + b;');× (不可)○ (可能)グローバルスコープ– 文字列から関数を生成
– パフォーマンスが低い
– セキュリティリスクあり
動的な関数生成
JSONPなど特殊な用途
ジェネレータ関数function* gen() { yield 1; }○ (可能)× (不可)呼び出し元に依存– 反復処理の制御
– yieldで値を返す
– 状態を保持
非同期処理
大量データの逐次処理

基本的な呼び出し

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

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); // こんにちは、花子さん!

パラメータと引数

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

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

パラメータタイプ構文例特徴使用場面
必須パラメータfunction(a, b)– 呼び出し時に必須
– 不足時はundefined
基本的な関数定義
デフォルトパラメータfunction(a = 1, b = 2)– 値が未提供時のデフォルト値
– ES6で導入
オプショナルな引数
残余パラメータfunction(...args)– 任意の数の引数を配列で受け取る
– 最後のパラメータのみ使用可能
可変長引数関数
分割代入パラメータfunction({x, y})– オブジェクトのプロパティを直接受け取る
– 設定オブジェクトに有用
設定オブジェクトの受け取り

必須パラメータ

関数の呼び出し時に必須となる引数です。

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

console.log(add(3, 5)); // 8

デフォルトパラメータ(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を実装しなさい。
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;
    };
}