複数の引数を扱う関数
複数の引数を定義する意義と基本メカニズム
プログラミングにおける関数とは、特定のタスクを実行するための再利用可能なコードブロックです。ソースによれば、関数はfunctionキーワードを使って宣言し、引数(ひきすう)を受け取ることができ、値を返すことも可能です。この「引数」こそが、関数というブラックボックスに対して外部からデータを渡すための唯一の入り口となります。特に、実用的なアプリケーション開発においては、単一の情報だけで処理が完結することは稀であり、複数の情報を組み合わせて計算や判断を行う必要があります。そのため、複数の引数を適切に扱う能力は、JavaScriptプログラミングにおいて必須のスキルと言えます。
基本的な構文として、複数の引数を受け取る関数は、カンマ区切りで仮引数(パラメータ)を定義します。ソースで紹介されているadd(a, b)関数は、その最もシンプルで基本的な例です。
function add(a, b) {
return a + b;
}
この定義において、aとbは関数の中でだけ使える変数(ローカル変数)として振る舞います。関数が呼び出される際、例えばadd(3, 4)と書くと、呼び出し側の「3」がaに、「4」がbにそれぞれ入った状態で関数内の処理が実行されます。この仕組みにより、開発者は具体的な数値に依存しない「2つの値を足す」というロジックを一度だけ作り、さまざまな場面で使い回せるようになります。
なぜ複数の引数が必要なのでしょうか。それは、関数が扱うタスクが現実的には複雑だからです。例えば、ソースで紹介されているNode.jsのサーバー作成コードを見てみましょう。
const server = http.createServer((request, response) => { … });
ここでは、アクセスが来たときに実行される関数(コールバック関数)が、requestとresponseという2つの引数を受け取ります。requestには「どのページを、どんな方法で要求しているか」といった入力情報が入り、responseには「何を返すか」「どんなヘッダーを付けるか」といった返答のための操作がまとまっています。もし引数が1つしかなければ、この2種類の役割を同時に扱うのが難しくなります。入力情報と出力操作を別々の引数として受け取れるからこそ、関数の中の処理が整理され、分かりやすく書けるのです。
また、引数の順番も重要です。JavaScriptでは、呼び出すときに渡した値(実引数)が、定義した仮引数の順番通りに割り当てられます。add(3, 4)なら、最初の3がaに、次の4がbに入ります。引数が増えるほど、呼び出し側が順番を間違えるリスクは上がりますが、この「順番で渡される」というルール自体は厳格に決まっています。ソースにあるように、JavaScriptはECMAScriptという標準仕様に基づいて進化しており、ES5からES6以降で書き方は増えましたが、「カンマ区切りで複数の値を渡す」という基本の仕組みは初期から変わりません。複数の引数を正しく理解し整理して扱えるようになることが、コードの複雑さをコントロールする第一歩になります。
動的型付け言語における引数の型の落とし穴
JavaScriptで複数の引数を扱うときに特に注意したいのが「データ型」です。ソースでも指摘されている通り、JavaScriptは動的型付け言語で、変数が数値なのか文字列なのかを事前に宣言しなくても動きます。この柔軟さは便利ですが、関数に想定外の型が渡ると、重大なバグにつながります。
例えば、add(a, b)に数値と文字列が混ざって渡された場合を考えます。
function add(a, b) {
return a + b;
}
const result = add(‘5’, 3);
静的型付け言語なら「型が違う」と実行前に止めてくれることがありますが、JavaScriptはエラーを出さずに動きます。そして+演算子のルールにより、数値を文字列へ変換して結合し、8ではなく”53″を返します。関数addは「足し算」を意図していても、呼び出し側が型を一つ間違えるだけで、処理は正しく動いているように見えつつ意味が壊れる、というのが落とし穴です。
さらに厄介なのが真偽値です。ソースにある通り、true + 1は2になります。もし権限レベルを計算するcalculateLevel(baseScore, bonusFlag)のような関数で、bonusFlagがうっかり数値扱いされると、意図しない権限が付与されるなど危険なバグにつながりかねません。
こうした事故を防ぐため、複数引数の関数では「型チェック」を入れるのが有効です。ソースで解説されているtypeof演算子を使えば、引数が期待した型かどうか確認できます。
function add(a, b) {
if (typeof a !== ‘number’ || typeof b !== ‘number’) {
console.error(“引数は数値である必要があります”);
return null;
}
return a + b;
}
関数の冒頭で引数の妥当性を検証(バリデーション)しておくと、JavaScript特有の「ゆるさ」によるバグを減らせます。ソースで推奨されている厳密等価演算子(===)も重要で、nullやundefinedの混入を厳密にチェックできるため、関数の安全性が上がります。引数が増えるほど「想定外データが入る入り口」も増えるので、型への意識は単一引数以上に重要です。
アロー関数における複数引数の記述と省略記法
現代のJavaScript(ES6以降)では、アロー関数がよく使われます。ソースでも解説されている通り、=>で関数式を短く書けますが、引数の数によって書き方が変わるのでルールを押さえておく必要があります。
引数が複数の場合、アロー関数では引数リストを()で囲みます。
const add = (a, b) => {
return a + b;
};
これは従来のfunction(a, b) { … }を置き換える書き方です。ES6で導入されたことで、特に無名関数をその場で渡すケース(コールバックなど)が短く書けるようになりました。
一方で、引数が1つだけなら()を省略できます。
const greet = name => { … };
便利ですが、初心者が混乱しやすいのが「引数0個」と「引数複数」です。この2つは()が必須です。
・引数なし:const sayHello = () => { console.log(“Hello”); };
・引数複数:const add = (a, b) => { return a + b; };
さらに、処理が1行でreturnするだけなら、{}とreturnを省略できます(暗黙のreturn)。
const add = (a, b) => a + b;
この形は、複数の引数を受け取って加工した結果を返すだけの関数(純粋関数)で特に強力です。mapやfilterのような高階関数と組み合わせると、コードが読みやすくなります。
ただし、アロー関数はthisの扱いに注意が必要です。functionで定義した関数と違い、アロー関数は自分のthisを持たず、定義した場所のthisを引き継ぎます。そのためメソッドやコンストラクタには向かない場面もありますが、データを受け取り加工して返す「ユーティリティ関数」としては非常に便利です。
オブジェクトを引数として渡す:実質的な名前付き引数
引数が増えてくると、呼び出し側が分かりにくくなる問題が出ます。例えば次のような関数です。
function registerUser(name, age, email, address, phone, gender) { … }
引数が多いと、registerUser(“太郎”, 25, “test@example.com“, …)のように順番を間違えないように書く必要があり、ミスが起きやすくなります。さらに「住所は省略OK」などオプションがあると、位置合わせのためにnullやundefinedを渡す必要が出て、コードが読みにくくなります。
そこでよく使われるのが「オブジェクトを1つ渡す」方法です。ソースでも、オブジェクトはキーと値のペアでデータをまとめられると解説されています。
function registerUser(userData) {
console.log(userData.name);
console.log(userData.email);
}
registerUser({
name: “太郎”,
email: “test@example.com“,
age: 25
});
こうすると受け取る側はuserData.nameのようにキー名で取り出せるため、順番を気にしなくてよくなり、読みやすさが上がります。これは他言語の「名前付き引数」に近い考え方です。
さらにES6の分割代入(Destructuring)を使えば、受け取り側でいきなり展開できます。
function registerUser({ name, age, email }) {
// name, age, email をそのまま使える
}
呼び出し側はオブジェクトを渡しているのに、関数内では変数として扱えるので便利です。APIレスポンスのようにプロパティが多いデータでも、必要なものだけ取り出せます。複数引数を並べるより、意味のある単位(オブジェクト)でまとめて渡す方が、変更にも強く保守しやすい設計になります。
テンプレートリテラルと引数の活用
関数が複数の引数を受け取ったあと、それらを組み合わせて結果を作る場面では、文字列操作がよく出てきます。そこで役立つのが、ソースでも詳しく解説されているテンプレートリテラルです。
テンプレートリテラルは、バッククォート( )で囲んだ文字列の中に、${expression}で変数や式を埋め込めます。+で結合する方法より、複数引数のときほど読みやすくなります。
例:
・従来:”Fifteen is ” + (a + b) + ” and not ” + (2 * a + b) + “.”
・テンプレート:Fifteen is ${a + b} and not ${2 * a + b}.
例えば「氏名」と「年齢」を受け取って文章を返すなら次のように書けます。
const greet = (name, age) => {
return ${name}さんは現在${age}歳です。;
};
引数が増えるほど、+での結合はスペースや引用符のミスが増えやすいので、テンプレートリテラルの恩恵が大きくなります。
さらにテンプレートリテラルには「タグ付きテンプレート」という高度な機能もあり、関数名...の形で書くと、文字列部分と埋め込み値が分解されて関数に引数として渡されます。
function myTag(strings, personExp, ageExp) { … }
const output = myTagThat ${person} is a ${age}.;
この仕組みを使うと、多言語化やHTMLエスケープのような加工を関数側で行えます。ソースではLaTeXテキスト処理の例もあり、複数の埋め込み値をまとめて加工できる強力な手段だと分かります。テンプレートリテラルは文字列を作るだけでなく、「関数への渡し方」を広げる機能も持っています。
スコープと引数の関係:ローカル変数としての振る舞い
関数に渡された引数は、関数内ではどう扱われるのでしょうか。ここを理解するには、ソースでも解説されているスコープ(有効範囲)が重要です。
結論として、関数の引数は関数内だけで使えるローカル変数として扱われます。
function example(arg) {
arg = “updated”;
console.log(arg); // “updated”
}
let globalVal = “original”;
example(globalVal);
console.log(globalVal); // “original”
このように、関数内でargを書き換えても、呼び出し元のglobalValは変わりません(プリミティブ型の場合)。引数は関数スコープに属するため、外側を汚さず安全に使えます。
ただし注意が必要なのは、オブジェクトや配列を渡す場合です。これらは参照渡しのように見える挙動(参照の値渡し)になります。つまり、関数に渡されるのは「データそのもの」ではなく「そのデータがある場所の情報」です。そのため、関数内でプロパティを変更すると、呼び出し元にも反映されます。
function modifyUser(user) {
user.name = “変更後の名前”;
}
const myUser = { name: “元の名前” };
modifyUser(myUser);
console.log(myUser.name); // “変更後の名前”
複数引数としてオブジェクトを受け取ると、この「意図せぬ書き換え」がバグになりやすいので注意が必要です。ソースで触れられているカプセル化やクロージャは、こうした変更をコントロールするための考え方でもあります。関数設計では、引数を「変更するのか」「読むだけか」を意識し、必要ならコピーしてから操作する配慮が必要です。
またES6のletによるブロックスコープをイメージすると、関数本体は{…}のブロックで、引数はそのブロックの先頭で宣言された変数のようなものだと考えられます。そうすると、有効範囲の感覚がつかみやすくなります。
コールバック関数への引数渡し:非同期処理の要
JavaScriptでは、関数を引数として渡すコールバック関数の形が頻繁に登場します。特に非同期処理では、「コールバックに何が渡されるか」を理解することが重要です。
ソースのfetchDataの例を見てみましょう。
function fetchData(callback) {
setTimeout(() => {
const data = “データ”;
callback(data);
}, 1000);
}
fetchDataはcallback(関数)を受け取り、非同期処理が終わった後にcallbackを実行します。ここで大事なのが callback(data) で、取得したdataを引数としてコールバックへ渡している点です。呼び出し側は次のように書きます。
fetchData((receivedData) => {
console.log(receivedData);
});
このreceivedDataに、fetchDataから渡されたdataが入ります。つまりコールバックの引数は、非同期処理の「結果」を受け取る器です。
同じ仕組みはDOMイベントでも使われます。
button.addEventListener(“click”, (event) => { … });
通常、イベントリスナーのコールバックには第一引数としてイベントオブジェクトが自動で渡されます。これによりクリック位置やキー情報などの詳細を受け取れます。
またPromiseでも、resolve(data)で渡したdataが、その後の.then((data) => …)の引数として渡されます。非同期処理では、前の処理の結果が次の関数の引数へとリレーされる形になるため、このデータの流れを追えるようになることが重要です。
コンストラクタ関数とクラスにおける引数:初期状態の設定
オブジェクト指向では、オブジェクトを作るときに初期状態を設定する仕組みがコンストラクタです。ここでも複数の引数が活躍します。
ソースの例ではStudentというコンストラクタ関数が定義されています。
var Student = function(name, id) {
this.name = name;
this.id = id;
…
};
new Student(“Samurai Taro”, 1000)のように呼ぶと、”Samurai Taro”と1000が引数として渡され、作られるオブジェクトのthis.nameとthis.idに入ります。引数があるからこそ、「名前もIDも違う学生」を同じ設計図から量産できます。
またメソッドも関数なので引数を取れます。例えばsetNameは、新しい名前を引数で受け取り、内部状態を更新するために使われます。
this.setName = function(_name) {
name = ‘Mr.’ + _name;
}
オブジェクト指向では、外から直接プロパティを書き換えるより、メソッドの引数として値を渡し、内部で加工して保存する「カプセル化」が重視されます。引数は、オブジェクトの中へ安全にデータを渡すための正規ルートとして働きます。
DOM操作APIにおける引数の役割
DOM操作のAPIは、JavaScriptの中でも特に引数が多く登場する分野です。ブラウザのメソッドは、必要な引数を渡してはじめて動作します。
getElementByIdは、ID文字列を1つ受け取ります。
const button = document.getElementById(‘the-button’);
querySelectorは、CSSセレクタを引数に取り、書き方次第でID・クラス・タグなど色々選べます。
要素の作成や追加では、複数引数のメソッドも出てきます。insertAdjacentElementは2つの引数が必要です。
element.insertAdjacentElement(‘beforebegin’, image);
第1引数は「どこに入れるか」を表す文字列、第2引数は「入れたい要素」です。順番を間違えたり無効な文字列を渡すと、エラーになったり表示位置がずれたりします。
またsetAttributeも典型的な2引数です。
element.setAttribute(‘title’, ‘Hello world’);
第1引数が属性名、第2引数が値です。このようにDOM APIは「対象」「設定内容」「オプション」など複数情報を引数で受け取る設計になっています。クライアントサイドJavaScriptではAPIごとの引数ルールを覚える必要がありますが、基本は「関数にデータを渡して処理をお願いする」だけだと捉えると理解しやすくなります。
サーバーサイド(Node.js)における引数の実践
Node.jsによってJavaScriptはサーバーサイドでも使えるようになりましたが、そこでも引数は中心的な役割を担います。ソースのHTTPサーバー作成例を見てみましょう。
const server = http.createServer((req, res) => {
res.writeHead(200, { ‘Content-Type’: ‘text/html’ });
res.write(‘
Hello World
‘);
res.end();
});
このコールバック関数のres(レスポンス)は、返答を制御する大きなオブジェクトです。res.writeHead(200, { … })では、1つ目の引数にHTTPステータスコード、2つ目にヘッダー情報のオブジェクトを渡しています。数値とオブジェクトという異なる型を複数引数で渡すことで、「正常なHTML応答です」という情報をまとめて伝えています。
またfs.readFileSyncのように引数が1つのAPIもあります。
fs.readFileSync(‘index.html’);
ただしオプションとしてエンコーディングを第2引数に渡せるなど、Node.jsのAPIは「必須引数+任意オプション+(非同期なら)コールバック」という形が多いです。
サーバーサイドでは、DB接続情報やAPIキーなど環境ごとに違う設定値を引数として渡して初期化する設計(依存性の注入)もよくあります。Expressでも app.get(‘/’, (req, res) => { … }) のように、パスとハンドラ関数を引数として渡す形が基本です。結局、フロントでもバックでも「複数の引数を定義し、呼び出し、受け取る」という関数の基本は変わりません。この仕組みを身につけることが、JavaScript開発の土台になります。
