404 Not Found

404 Not Found


nginx

JavaScriptの関数

関数は再利用可能なコードブロックです。一度書けば何度でも使えます。レシピのようなもので、手順を一度定義すれば、料理するたびに従えます。関数を使わないプログラマーは、毎回料理をゼロから考えるようなもの — 疲労困憊で間違いも増えます。

関数とは?

関数はロジックの一部を「パッケージ化」し、名前を付け、必要なときに呼び出せるようにします:

HTML
<!DOCTYPE html>
<html lang="ja">
<head><meta charset="UTF-8"><title>関数の呼び出し</title></head>
<body>
<div id="output"></div>
<script>
function sayHello() {
  document.getElementById('output').textContent += 'こんにちは!\n';
}

sayHello();
sayHello();
</script>
</body>
</html>

関数のメリット:

関数宣言と関数式

関数を作成する主な2つの方法があります:

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

// 関数式
let greet2 = function(name) {
  return `こんにちは、${name}さん!`;
};
</script>

重要な違い:関数宣言はホイスティングされる — コード中で関数が登場する前に呼び出すことができます。関数式はホイスティングされないため、先に定義する必要があります。

HTML
<script>
// 関数宣言:定義前に呼び出し — 正常に動作
hello();  // エラーなしで実行
function hello() { console.log('こんにちは'); }

// 関数式:定義前に呼び出し — エラー
// hi();     // TypeError: hi is not a function
// let hi = function() { console.log('こんにちは'); };
</script>
⚠️ let変数に宣言前にアクセスすると、「テンポラルデッドゾーン」(TDZ)エラーが発生します。undefinedにはなりません。関数式は必ず使用前に定義しましょう。

パラメータとデフォルトパラメータ

関数はパラメータを通じて外部データを受け取ります:

HTML
<script>
function add(a, b) {
  return a + b;
}
add(1, 2);  // 3
</script>

渡さなかったパラメータはundefinedになります。ES6ではデフォルトパラメータが導入されました:

HTML
<script>
function greet(name, greeting = 'こんにちは') {
  return `${greeting}、${name}さん!`;
}

greet('花子');           // 'こんにちは、花子さん!'
greet('花子', 'やあ');   // 'やあ、花子さん!'
</script>

💪 デフォルトパラメータはリストの最後に配置しましょう。最初に置いても意味がなく、呼び出し元は後続のパラメータをスキップできません。

戻り値

returnは結果を呼び出し元に送り、関数を即座に停止します:

HTML
<script>
function square(n) {
  return n * n;
}

let result = square(5);  // 25
</script>

returnがない関数(またはreturn;だけの関数)はundefinedを返します。

HTML
<script>
function doNothing() {
  // returnなし
}
doNothing();  // undefined
</script>
⚠️ returnの後に改行すると、自動セミコロン挿入が発生し、undefinedを返します:

return     // return; undefined になる
  obj;     // この行は実行されない

対策:戻り値を同じ行に書くか、括弧で囲みましょう。

アロー関数(=>)

アロー関数はES6で導入された短い構文です:

HTML
<script>
// 通常の関数
let double = function(n) {
  return n * 2;
};

// アロー関数
let double2 = (n) => n * 2;

// 複数の文には波括弧と明示的なreturnが必要
let greet = (name) => {
  let msg = `こんにちは、${name}さん!`;
  return msg;
};
</script>

構文ルール:

実例:基本的な関数の使い方

HTML
<!DOCTYPE html>
<html>
<body>
  <h2>基本的な関数の使い方</h2>
  <div id="output"></div>
  <script>
    function add(a, b) {
      return a + b;
    }

    function greet(name, greeting) {
      if (greeting === undefined) greeting = 'こんにちは';
      return greeting + '、' + name + 'さん!';
    }

    let multiply = (a, b) => a * b;

    let square = n => n * n;

    let html = '';
    html += 'add(3, 5) = ' + add(3, 5) + '<br>';
    html += 'add(10, 20) = ' + add(10, 20) + '<br>';
    html += "greet('花子') = " + greet('花子') + '<br>';
    html += "greet('花子', 'やあ') = " + greet('花子', 'やあ') + '<br>';
    html += 'multiply(4, 6) = ' + multiply(4, 6) + '<br>';
    html += 'square(7) = ' + square(7) + '<br>';

    document.getElementById('output').innerHTML = html;
  </script>
</body>
</html>

実例:シンプルな電卓

HTML
<!DOCTYPE html>
<html>
<body>
  <h2>シンプルな電卓</h2>
  <input type="number" id="numA" placeholder="数値1">
  <select id="op">
    <option value="+">+</option>
    <option value="-">-</option>
    <option value="*">&times;</option>
    <option value="/">&divide;</option>
  </select>
  <input type="number" id="numB" placeholder="数値2">
  <button onclick="calc()">計算</button>
  <p id="result"></p>
  <script>
    function calc() {
      let a = Number(document.getElementById('numA').value);
      let b = Number(document.getElementById('numB').value);
      let op = document.getElementById('op').value;
      let res;

      switch (op) {
        case '+': res = a + b; break;
        case '-': res = a - b; break;
        case '*': res = a * b; break;
        case '/':
          if (b === 0) {
            document.getElementById('result').innerHTML = 'ゼロでは割れません!';
            return;
          }
          res = a / b;
          break;
      }

      document.getElementById('result').innerHTML =
        a + ' ' + op + ' ' + b + ' = <strong>' + res + '</strong>';
    }
  </script>
</body>
</html>

関数スコープ

関数内で宣言された変数はローカルで、その関数内にのみ存在します。これがスコープです:

HTML
<script>
function test() {
  let x = 10;    // ローカル変数
  const y = 20;
  var z = 30;
}
test();
// console.log(x);  // ReferenceError: x is not defined
</script>

関数は外側の変数にアクセスできます(クロージャーの基礎)が、外側のコードは関数内の変数にアクセスできません:

HTML
<!DOCTYPE html>
<html lang="ja">
<head><meta charset="UTF-8"><title>関数スコープ</title></head>
<body>
<pre id="output"></pre>
<script>
let global = 'グローバル変数';

function show() {
  let local = 'ローカル変数';
  document.getElementById('output').textContent +=
    '関数内 — global: ' + global + '\n' +
    '関数内 — local: ' + local + '\n';
}
show();
document.getElementById('output').textContent +=
  '関数外 — global: ' + global + '\n' +
  '関数外 — local: アクセス不可(ローカル変数)';
</script>
</body>
</html>
⚠️ 関数内でlet/const/varなしで変数に代入すると、グローバル変数が作成されます。よくあるバグの原因なので、常にletまたはconstで変数を宣言しましょう。

実例:スコープデモ

HTML
<!DOCTYPE html>
<html>
<body>
  <h2>関数スコープ</h2>
  <div id="output"></div>
  <script>
    let outer = '外側の変数';

    function demo() {
      let inner = '内側の変数';
      let html = '';
      html += '関数内 — outer: ' + outer + '<br>';
      html += '関数内 — inner: ' + inner + '<br>';

      function nested() {
        let deep = 'ネストした変数';
        html += 'ネスト関数 — outer: ' + outer + '<br>';
        html += 'ネスト関数 — inner: ' + inner + '<br>';
        html += 'ネスト関数 — deep: ' + deep + '<br>';
      }
      nested();
      return html;
    }

    let result = demo();
    result += '関数外 — outer: ' + outer + '<br>';
    result += '関数外 — inner: アクセス不可(ローカル)';

    document.getElementById('output').innerHTML = result;
  </script>
</body>
</html>

コールバック入門

JavaScriptでは、関数を別の関数の引数として渡すことができます。渡された関数をコールバックと呼びます:

HTML
<!DOCTYPE html>
<html lang="ja">
<head><meta charset="UTF-8"><title>コールバック</title></head>
<body>
<pre id="output"></pre>
<script>
function processArray(arr, callback) {
  for (let item of arr) {
    callback(item);
  }
}

let names = ['花子', '太郎', '一郎'];
processArray(names, function(name) {
  document.getElementById('output').textContent += 'こんにちは、' + name + 'さん\n';
});
</script>
</body>
</html>

すでにコールバックを使ったことがあります — forEachmapfilterの引数はすべてコールバック関数です:

HTML
<script>
// ここの無名関数はコールバック
[1, 2, 3].forEach(function(n) {
  console.log(n);
});

// アロー関数でより簡潔に
[1, 2, 3].forEach(n => console.log(n));
</script>

💪 コールバックはJavaScriptの魂です。イベントハンドラ、タイマー、ネットワークリクエストはすべてコールバックメカニズムに依存しています。今のところ「関数は引数として渡せる」とだけ覚えておけば十分です。後ほど深く掘り下げます。

実例:コールバックの実践

HTML
<!DOCTYPE html>
<html>
<body>
  <h2>コールバック関数</h2>
  <div id="output"></div>
  <script>
    function processList(items, action) {
      let results = [];
      for (let item of items) {
        results.push(action(item));
      }
      return results;
    }

    let nums = [1, 2, 3, 4, 5];

    let doubled = processList(nums, n => n * 2);
    let squared = processList(nums, n => n * n);
    let labeled = processList(nums, n => 'アイテム ' + n);

    let html = '';
    html += '元の配列: [' + nums.join(', ') + ']<br><br>';

    html += '<strong>コールバック: n =&gt; n * 2</strong><br>';
    html += '結果: [' + doubled.join(', ') + ']<br><br>';

    html += '<strong>コールバック: n =&gt; n * n</strong><br>';
    html += '結果: [' + squared.join(', ') + ']<br><br>';

    html += '<strong>コールバック: n =&gt; "アイテム " + n</strong><br>';
    html += '結果: [' + labeled.join(', ') + ']<br><br>';

    html += '<strong>setTimeoutコールバック(2秒後に表示):</strong><br>';
    html += '<span id="timer">待機中...</span>';

    document.getElementById('output').innerHTML = html;

    setTimeout(function() {
      document.getElementById('timer').textContent = "時間切れ!コールバックが実行されました。";
      document.getElementById('timer').style.color = 'green';
    }, 2000);
  </script>
</body>
</html>

📖 まとめ

  1. 関数宣言はホイスティングされるが、関数式はされない — 使用前に定義するのが最も安全
  2. デフォルトパラメータは柔軟性を高め、パラメータリストの末尾に配置する
  3. returnは関数を終了させ、値を返す。なければundefinedを返す
  4. アロー関数(a, b) => a + bは省略記法 — 1行は自動的に戻し、複数行は{}が必要
  5. 関数内で宣言された変数はローカルで、外からは見えない
  6. コールバック = 関数を引数として渡す — forEachsetTimeoutなどで使用

❓ よくある質問

Q アロー関数と通常の関数の違いは何ですか?
A 短い構文に加え、アロー関数は独自のthisを持ちません。周囲のスコープから継承します。thisが現在のオブジェクトを指す必要がある場合(メソッドやイベントハンドラでは)通常の関数を使います。シンプルなユーティリティ関数にはアロー構文が適しています。
Q 関数は複数の値を返せますか?
A 直接は返せませんが、配列やオブジェクトを返して分割代入できます:function getInfo() { return [name, age]; } — 呼び出しはlet [name, age] = getInfo()のようにします。
Q パラメータが多すぎるとどうしますか?
A パラメータが3つ以上の場合は、オブジェクトの使用を検討しましょう:function createUser({ name, age, city }) {} — 呼び出しはcreateUser({ name: '花子', age: 20, city: '東京' })のようにします。順序は問われず、はるかに読みやすくなります。

📝 演習

  1. 数値が偶数ならtrue、それ以外はfalseを返す関数isEven(n)を書いてください。複数の値でテストしてページに表示してください。
  2. 数値を受け取りフォーマットされた価格文字列を返す関数formatPrice(price)を書いてください(例:formatPrice(9.9)"¥9.90"を返す)。アロー関数構文を使用してください。
  3. 「あいさつジェネレーター」を作成してください:名前の配列とあいさつ関数を受け取るコールバック関数を使い、各人にパーソナライズされたあいさつをページに表示してください。
100%