JavaScriptの関数
関数は再利用可能なコードブロックです。一度書けば何度でも使えます。レシピのようなもので、手順を一度定義すれば、料理するたびに従えます。関数を使わないプログラマーは、毎回料理をゼロから考えるようなもの — 疲労困憊で間違いも増えます。
関数とは?
関数はロジックの一部を「パッケージ化」し、名前を付け、必要なときに呼び出せるようにします:
<!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>
関数のメリット:
- 再利用性:一度書けばどこからでも呼び出せる
- カプセル化:複雑さを隠し、インターフェースだけを公開する
- 保守性:1箇所を変えれば、すべての呼び出し元に恩恵がある
関数宣言と関数式
関数を作成する主な2つの方法があります:
<script>
// 関数宣言
function greet(name) {
return `こんにちは、${name}さん!`;
}
// 関数式
let greet2 = function(name) {
return `こんにちは、${name}さん!`;
};
</script>
重要な違い:関数宣言はホイスティングされる — コード中で関数が登場する前に呼び出すことができます。関数式はホイスティングされないため、先に定義する必要があります。
<script>
// 関数宣言:定義前に呼び出し — 正常に動作
hello(); // エラーなしで実行
function hello() { console.log('こんにちは'); }
// 関数式:定義前に呼び出し — エラー
// hi(); // TypeError: hi is not a function
// let hi = function() { console.log('こんにちは'); };
</script>
let変数に宣言前にアクセスすると、「テンポラルデッドゾーン」(TDZ)エラーが発生します。undefinedにはなりません。関数式は必ず使用前に定義しましょう。
パラメータとデフォルトパラメータ
関数はパラメータを通じて外部データを受け取ります:
<script>
function add(a, b) {
return a + b;
}
add(1, 2); // 3
</script>
渡さなかったパラメータはundefinedになります。ES6ではデフォルトパラメータが導入されました:
<script>
function greet(name, greeting = 'こんにちは') {
return `${greeting}、${name}さん!`;
}
greet('花子'); // 'こんにちは、花子さん!'
greet('花子', 'やあ'); // 'やあ、花子さん!'
</script>
💪 デフォルトパラメータはリストの最後に配置しましょう。最初に置いても意味がなく、呼び出し元は後続のパラメータをスキップできません。
戻り値
returnは結果を呼び出し元に送り、関数を即座に停止します:
<script>
function square(n) {
return n * n;
}
let result = square(5); // 25
</script>
returnがない関数(またはreturn;だけの関数)はundefinedを返します。
<script>
function doNothing() {
// returnなし
}
doNothing(); // undefined
</script>
returnの後に改行すると、自動セミコロン挿入が発生し、undefinedを返します:
return // return; undefined になる
obj; // この行は実行されない
対策:戻り値を同じ行に書くか、括弧で囲みましょう。
アロー関数(=>)
アロー関数はES6で導入された短い構文です:
<script>
// 通常の関数
let double = function(n) {
return n * 2;
};
// アロー関数
let double2 = (n) => n * 2;
// 複数の文には波括弧と明示的なreturnが必要
let greet = (name) => {
let msg = `こんにちは、${name}さん!`;
return msg;
};
</script>
構文ルール:
- パラメータ1つの場合:括弧は省略可能 —
n => n * 2 - パラメータ0個または複数:括弧が必要 —
() => 'こんにちは'、(a, b) => a + b - 1行の式:自動的に戻す。複数行:
{}とreturnが必要
実例:基本的な関数の使い方
<!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>
実例:シンプルな電卓
<!DOCTYPE html>
<html>
<body>
<h2>シンプルな電卓</h2>
<input type="number" id="numA" placeholder="数値1">
<select id="op">
<option value="+">+</option>
<option value="-">-</option>
<option value="*">×</option>
<option value="/">÷</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>
関数スコープ
関数内で宣言された変数はローカルで、その関数内にのみ存在します。これがスコープです:
<script>
function test() {
let x = 10; // ローカル変数
const y = 20;
var z = 30;
}
test();
// console.log(x); // ReferenceError: x is not defined
</script>
関数は外側の変数にアクセスできます(クロージャーの基礎)が、外側のコードは関数内の変数にアクセスできません:
<!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で変数を宣言しましょう。
実例:スコープデモ
<!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では、関数を別の関数の引数として渡すことができます。渡された関数をコールバックと呼びます:
<!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>
すでにコールバックを使ったことがあります — forEach、map、filterの引数はすべてコールバック関数です:
<script>
// ここの無名関数はコールバック
[1, 2, 3].forEach(function(n) {
console.log(n);
});
// アロー関数でより簡潔に
[1, 2, 3].forEach(n => console.log(n));
</script>
💪 コールバックはJavaScriptの魂です。イベントハンドラ、タイマー、ネットワークリクエストはすべてコールバックメカニズムに依存しています。今のところ「関数は引数として渡せる」とだけ覚えておけば十分です。後ほど深く掘り下げます。
実例:コールバックの実践
<!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 => n * 2</strong><br>';
html += '結果: [' + doubled.join(', ') + ']<br><br>';
html += '<strong>コールバック: n => n * n</strong><br>';
html += '結果: [' + squared.join(', ') + ']<br><br>';
html += '<strong>コールバック: n => "アイテム " + 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>
📖 まとめ
- 関数宣言はホイスティングされるが、関数式はされない — 使用前に定義するのが最も安全
- デフォルトパラメータは柔軟性を高め、パラメータリストの末尾に配置する
returnは関数を終了させ、値を返す。なければundefinedを返す- アロー関数
(a, b) => a + bは省略記法 — 1行は自動的に戻し、複数行は{}が必要 - 関数内で宣言された変数はローカルで、外からは見えない
- コールバック = 関数を引数として渡す —
forEach、setTimeoutなどで使用
❓ よくある質問
thisを持ちません。周囲のスコープから継承します。thisが現在のオブジェクトを指す必要がある場合(メソッドやイベントハンドラでは)通常の関数を使います。シンプルなユーティリティ関数にはアロー構文が適しています。function getInfo() { return [name, age]; } — 呼び出しはlet [name, age] = getInfo()のようにします。function createUser({ name, age, city }) {} — 呼び出しはcreateUser({ name: '花子', age: 20, city: '東京' })のようにします。順序は問われず、はるかに読みやすくなります。📝 演習
- 数値が偶数なら
true、それ以外はfalseを返す関数isEven(n)を書いてください。複数の値でテストしてページに表示してください。 - 数値を受け取りフォーマットされた価格文字列を返す関数
formatPrice(price)を書いてください(例:formatPrice(9.9)は"¥9.90"を返す)。アロー関数構文を使用してください。 - 「あいさつジェネレーター」を作成してください:名前の配列とあいさつ関数を受け取るコールバック関数を使い、各人にパーソナライズされたあいさつをページに表示してください。



